Compositor lowers the resolution of the output

Hi!

I am kind of new with openRNDR and I am experimenting with the compositor in olive.
In the following code I have a bunch of lines being animated and then perturbed.

When not in the compositor (nocomposite.kt), the animation looks fine and I can see all the details. As soon as I insert it in a compositor (composite.kt) the level of detail goes away.

What am I getting wrong here?

// Nocomposite.kt


import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extra.compositor.blend
import org.openrndr.extra.compositor.compose
import org.openrndr.extra.compositor.draw
import org.openrndr.extra.compositor.layer
import org.openrndr.extra.fx.blend.Add
import org.openrndr.extra.fx.distort.DisplaceBlend
import org.openrndr.extra.fx.distort.Fisheye
import org.openrndr.extra.fx.distort.Perturb
import org.openrndr.extra.fx.edges.EdgesWork
import org.openrndr.extra.olive.oliveProgram
import org.openrndr.math.IntVector2
import org.openrndr.math.Vector2
import org.openrndr.shape.Circle
import org.openrndr.shape.contour
import kotlin.math.PI
import kotlin.math.cos


fun main() = application {
    configure {
        width = 800
        height = 800
        position = IntVector2(1920 / 2, 20)
    }
    oliveProgram {

        val c = contour {
            moveTo(0.0, 0.0)
            curveTo(100.0, 0.0, 200.0, 70.0)
            continueTo(50.0, 20.0)
            continueTo(50.0, 70.0)
            continueTo(30.0, 20.0)
            lineTo(30.0, 0.0)
            lineTo(100.0, 0.0)
            curveTo(0.0,30.0, 0.0, 0.0)
            close()
        }

        val filter = Perturb()
        val unfiltered = renderTarget(width, height) {
            colorBuffer()
            depthBuffer()
        }
        val filtered = renderTarget(width, height) {
            colorBuffer()
        }

        val composite = compose {
            draw {

                drawer.fill = null
                drawer.stroke = ColorRGBa.BLACK
                drawer.strokeWeight = 350.0
                drawer.circle(Circle(width / 2.0, height / 2.0, 650.0))
            }
            layer {
                blend(DisplaceBlend()) {
                }
                draw {

            }


            }

        }


        extend {
            drawer.stroke = ColorRGBa.WHITE
            for (y in 0 until 50) {
                for(x in 0 until 50) {


                    drawer.isolatedWithTarget(unfiltered) {


                        drawer.translate(x * 11.0, y * 50.0)

                        val o: Double = cos(seconds) * 0.35 + x / 0.01 - y * 0.01

                        filter.phase = cos(seconds) * 0.5
                        filter.decay = 0.168
                        filter.gain = cos(seconds * 2) * 0.5



                        drawer.contour(c.sub(o, o + 0.023))

                    }

                }
            }


            filter.apply(unfiltered.colorBuffer(0), filtered.colorBuffer(0))


            drawer.clear(ColorRGBa(0.0, 0.1, 0.6))

            drawer.image(filtered.colorBuffer(0))

            composite.draw(drawer)

        }
    }
}
// Composite.kt


fun main() = application {
    configure {
        width = 800
        height = 800
        position = IntVector2(1920 / 2, 20)
    }
    oliveProgram {

        val c = contour {
            moveTo(0.0, 0.0)
            curveTo(100.0, 0.0, 200.0, 70.0)
            continueTo(50.0, 20.0)
            continueTo(50.0, 70.0)
            continueTo(30.0, 20.0)
            lineTo(30.0, 0.0)
            lineTo(100.0, 0.0)
            curveTo(0.0,30.0, 0.0, 0.0)
            close()
        }

        val filter = Perturb()
        val unfiltered = renderTarget(width, height) {
            colorBuffer()
            depthBuffer()
        }
        val filtered = renderTarget(width, height) {
            colorBuffer()
        }

        val composite = compose {
            draw {

                drawer.fill = null
                drawer.stroke = ColorRGBa.BLACK
                drawer.strokeWeight = 350.0
                drawer.circle(Circle(width / 2.0, height / 2.0, 650.0))
            }
            layer {
                blend(DisplaceBlend()) {
                }
                draw {
                    drawer.stroke = ColorRGBa.WHITE
                    for (y in 0 until 50) {
                        for(x in 0 until 50) {


                            drawer.isolatedWithTarget(unfiltered) {


                                drawer.translate(x * 11.0, y * 50.0)

                                val o: Double = cos(seconds) * 0.35 + x / 0.01 - y * 0.01

                                filter.phase = cos(seconds) * 0.5
                                filter.decay = 0.168
                                filter.gain = cos(seconds * 2) * 0.5



                                drawer.contour(c.sub(o, o + 0.023))

                            }

                        }
                    }


                }


            }

        }


        extend {


            filter.apply(unfiltered.colorBuffer(0), filtered.colorBuffer(0))


            drawer.clear(ColorRGBa(0.0, 0.1, 0.6))

            drawer.image(filtered.colorBuffer(0))

            composite.draw(drawer)

        }
    }
}

Hi :slight_smile: welcome to the forum @marcoshier !

I’m not sure how you would like things to look like. Could you maybe share screenshots to see what you mean?

Meanwhile, I tried making some changes to the second program to make it look closer to the first one, but I don’t know if I got it right:

imports
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.color.rgb
import org.openrndr.draw.isolated
import org.openrndr.extra.compositor.*
import org.openrndr.extra.fx.blend.Add
import org.openrndr.extra.fx.distort.Perturb
import org.openrndr.math.IntVector2
import org.openrndr.shape.Circle
import org.openrndr.shape.contour
import kotlin.math.cos
fun main() = application {
    configure {
        width = 800
        height = 800
        position = IntVector2(1920 / 2, 20)
    }
    oliveProgram {

        val c = contour {
            moveTo(0.0, 0.0)
            curveTo(100.0, 0.0, 200.0, 70.0)
            continueTo(50.0, 20.0)
            continueTo(50.0, 70.0)
            continueTo(30.0, 20.0)
            lineTo(30.0, 0.0)
            lineTo(100.0, 0.0)
            curveTo(0.0, 30.0, 0.0, 0.0)
            close()
        }

        val composite = compose {
            draw {
                drawer.fill = rgb(0.0, 0.1, 0.6)
                drawer.circle(Circle(width / 2.0, height / 2.0, 350.0))
            }
            layer {
                blend(Add()) {
                    clip = true
                }
                draw {
                    drawer.stroke = ColorRGBa.WHITE
                    for (y in 0 until 50) {
                        for (x in 0 until 50) {
                            val o: Double =
                                cos(seconds) * 0.35 + x / 0.01 - y * 0.01

                            drawer.isolated {
                                translate(x * 11.0, y * 50.0)
                                contour(c.sub(o, o + 0.023))
                            }
                        }
                    }
                }
                post(Perturb()) {
                    phase = cos(seconds) * 0.5
                    decay = 0.168
                    gain = cos(seconds * 2) * 0.5
                }
            }
        }

        extend {
            drawer.clear(ColorRGBa.BLACK)
            composite.draw(drawer)
        }
    }
}

Did I get it close? Maybe post() can help?

Other ideas:

  • rgb() can be used, shorter than ColorRGBa()
  • Why the large strokeWeight instead of a fill? To draw a ring maybe? Not sure if they look or perform differently.
  • For better performance I would avoid calling isolatedWithTarget 2500 times. Instead we can call it once and put both loops inside it, then inside the loops:
drawer.isolated {
  translate(x * 11.0, y * 50.0)
  contour(c.sub(o, o + 0.023))
}
  • No need to update the filter settings many times if it’s being applied just once. Maybe better like this?
filter.run {
  phase = cos(seconds) * 0.5
  decay = 0.168
  gain = cos(seconds * 2) * 0.5
  apply(unfiltered.colorBuffer(0), filtered.colorBuffer(0))
}

I guess one can arrive less optimized code when experimenting while live coding :slight_smile: I’ve been there.

ps. Was it meant to be x / 0.01 or x * 0.01? The first one will give very large numbers, probably unsuitable for .sub()