From c388cbe7e2cdbf4d14ed938b45b9aef8de306f3d Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sun, 25 Jul 2021 12:09:09 +0200 Subject: [PATCH] [orx-temporal-blur] Add gain and color matrix controls --- orx-temporal-blur/README.md | 40 ++++++++----------- orx-temporal-blur/build.gradle | 15 +++++++ .../src/demo/kotlin/DemoBasic01.kt | 22 ++++++++++ .../src/demo/kotlin/DemoColorShift01.kt | 38 ++++++++++++++++++ .../src/main/kotlin/TemporalBlur.kt | 37 +++++++++++++++-- 5 files changed, 125 insertions(+), 27 deletions(-) create mode 100644 orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt create mode 100644 orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt diff --git a/orx-temporal-blur/README.md b/orx-temporal-blur/README.md index 94cf2c17..3f30615c 100644 --- a/orx-temporal-blur/README.md +++ b/orx-temporal-blur/README.md @@ -10,27 +10,6 @@ while processing the tail-end of the extension chain. This multi-sampling strate entirely suited in real-time and/or interactive settings. `orx-temporal-blur` works well with programs that use `seconds` for their animation input. -This includes `Animatables`, but only after the `Animatable` clock is synchronized with the `Program` clock. -(Which you should already have done when using `ScreenRecorder`) - -Synchronizing clocks in OPENRNDR 0.3.36 (current release): -``` -Animatable.clock(object: Clock { - override val time: Long - get() = (clock() * 1E3).toLong() - }) -``` - -Synchronizing high precision clocks in OPENRNDR 0.3.37 (future release) -``` -Animatable.clock(object: Clock { - override val time: Long - get() = timeNanos / 1000 - override val timeNanos: Long - get() = (clock() * 1E6).toLong() - - }) -``` Note that time-step-based simulations or integrations will likely break because your drawing code will be executed multiple times per frame. @@ -39,9 +18,24 @@ per frame. ```kotlin extend(TemporalBlur()) { - duration = 0.9 - samples = 30 + duration = 0.9 // duration is in frames + samples = 30 fps = 60.0 jitter = 1.0 } ``` + +## Color shifts + +Additionally, a color matrix can be set per accumulation step. See [`DemoColorShift01.kt`](src/demo/kotlin/DemoColorShift01.kt) + +``` +extend(TemporalBlur()) { + colorMatrix = { + // `it` is 0.0 at start of frame, 1.0 at end of frame + tint(ColorRGBa.WHITE.mix(ColorRGBa.BLUE, it)) + } +} +``` + + diff --git a/orx-temporal-blur/build.gradle b/orx-temporal-blur/build.gradle index 52b6d138..cfa43220 100644 --- a/orx-temporal-blur/build.gradle +++ b/orx-temporal-blur/build.gradle @@ -1,5 +1,20 @@ +sourceSets { + demo { + java { + srcDirs = ["src/demo/kotlin"] + compileClasspath += main.getCompileClasspath() + runtimeClasspath += main.getRuntimeClasspath() + } + } +} + dependencies { implementation project(":orx-noise") implementation project(":orx-fx") implementation("org.openrndr:openrndr-filter:$openrndrVersion") + demoImplementation("org.openrndr:openrndr-application:$openrndrVersion") + demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion") + demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion") + demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion") + demoImplementation(sourceSets.getByName("main").output) } \ No newline at end of file diff --git a/orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt b/orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt new file mode 100644 index 00000000..649eb34e --- /dev/null +++ b/orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt @@ -0,0 +1,22 @@ +import org.openrndr.applicationSynchronous +import org.openrndr.extensions.SingleScreenshot +import org.openrndr.extra.temporalblur.TemporalBlur +import org.openrndr.math.Polar + +fun main() = applicationSynchronous { + program { + if (System.getProperty("takeScreenshot") == "true") { + extend(SingleScreenshot()) { + this.outputFile = System.getProperty("screenshotPath") + } + } + extend(TemporalBlur()) { + samples = 10 + duration = 0.9 + } + + extend { + drawer.circle(Polar(seconds * 360.0, 200.0).cartesian + drawer.bounds.center, 50.0) + } + } +} \ No newline at end of file diff --git a/orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt b/orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt new file mode 100644 index 00000000..919af561 --- /dev/null +++ b/orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt @@ -0,0 +1,38 @@ +import org.openrndr.applicationSynchronous +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.tint +import org.openrndr.extensions.SingleScreenshot +import org.openrndr.extra.temporalblur.TemporalBlur +import org.openrndr.math.Polar +import kotlin.math.cos + +fun main() = applicationSynchronous { + program { + if (System.getProperty("takeScreenshot") == "true") { + extend(SingleScreenshot()) { + this.outputFile = System.getProperty("screenshotPath") + } + } + extend(TemporalBlur()) { + samples = 100 + duration = 10.0 + colorMatrix = { + // `it` is 0.0 at start of frame, 1.0 at end of frame + tint(ColorRGBa.WHITE.mix(ColorRGBa.BLUE, it)) + } + gain = 1.2 + } + + extend { + for (i in 0 until 10) { + drawer.circle( + Polar( + seconds * 760.0 + i * 30, + 140.0 + cos(i + seconds) * 40.0 + ).cartesian + drawer.bounds.center, + 50.0 + ) + } + } + } +} \ No newline at end of file diff --git a/orx-temporal-blur/src/main/kotlin/TemporalBlur.kt b/orx-temporal-blur/src/main/kotlin/TemporalBlur.kt index 175ddda4..e1da9b66 100644 --- a/orx-temporal-blur/src/main/kotlin/TemporalBlur.kt +++ b/orx-temporal-blur/src/main/kotlin/TemporalBlur.kt @@ -44,6 +44,7 @@ private val add by lazy { PlainAdd() } * the scene as many times as you have samples. */ class TemporalBlur : Extension { + private var oldClock: () -> Double = { 0.0 } override var enabled: Boolean = true private var accumulator: RenderTarget? = null @@ -51,6 +52,9 @@ class TemporalBlur : Extension { private var image: RenderTarget? = null private var imageResolved: RenderTarget? = null + // modifier for final stage averager, higher gain results in brighter images + var gain = 1.0 + /** * number of samples to take, more is slower */ @@ -86,6 +90,12 @@ class TemporalBlur : Extension { * */ var multisample: BufferMultisample = BufferMultisample.SampleCount(8) + + var colorMatrix: (Double)->Matrix55 = { Matrix55.IDENTITY } + + + var beforeDrawAccumulated : TemporalBlur.() -> Unit = {} + override fun beforeDraw(drawer: Drawer, program: Program) { val extensionOffset = program.extensions.indexOf(this) val extensionTail = program.extensions.drop(extensionOffset + 1) @@ -155,12 +165,11 @@ class TemporalBlur : Extension { drawer.clear(ColorRGBa.BLACK) } } - val oldClock = program.clock + oldClock = program.clock val oldClockValue = oldClock() for (i in samples - 1 downTo 1) { image?.bind() - drawer.clear(ColorRGBa.BLACK) program.clock = { oldClockValue - (i * duration) / (fps * samples) } @@ -194,8 +203,16 @@ class TemporalBlur : Extension { } } + val activeColorMatrix = colorMatrix(i / (samples - 1.0)) + if (activeColorMatrix !== Matrix55.IDENTITY) { + drawer.isolatedWithTarget(imageResolved!!) { + drawer.drawStyle.colorMatrix = activeColorMatrix + drawer.drawStyle.blendMode = BlendMode.REPLACE + drawer.image(imageResolved!!.colorBuffer(0)) + } + } add.apply(arrayOf(imageResolved!!.colorBuffer(0), accumulator!!.colorBuffer(0)), accumulator!!.colorBuffer(0)) - program.clock = oldClock + fsf.setDouble(program, program.clock()) } image?.let { @@ -207,11 +224,23 @@ class TemporalBlur : Extension { } override fun afterDraw(drawer: Drawer, program: Program) { + // restore clock + program.clock = oldClock // -- we receive one last frame here image?.unbind() image!!.colorBuffer(0).copyTo(imageResolved!!.colorBuffer(0)) + val activeColorMatrix = colorMatrix(0.0) + if (activeColorMatrix !== Matrix55.IDENTITY) { + drawer.isolatedWithTarget(imageResolved!!) { + drawer.drawStyle.colorMatrix = activeColorMatrix + drawer.drawStyle.blendMode = BlendMode.REPLACE + drawer.image(imageResolved!!.colorBuffer(0)) + } + } + add.apply(arrayOf(imageResolved!!.colorBuffer(0), accumulator!!.colorBuffer(0)), accumulator!!.colorBuffer(0)) + beforeDrawAccumulated() // -- render accumulated result drawer.isolated { @@ -222,7 +251,7 @@ class TemporalBlur : Extension { drawer.drawStyle.blendMode = BlendMode.OVER drawer.clear(ColorRGBa.BLACK) - drawer.drawStyle.colorMatrix = tint(ColorRGBa.WHITE.shade(1.0 / samples)) + drawer.drawStyle.colorMatrix = tint(ColorRGBa.WHITE.shade((1.0 / samples) * gain)) drawer.image(accumulator!!.colorBuffer(0)) } if (delinearizeOutput) {