[orx-temporal-blur] Add gain and color matrix controls
This commit is contained in:
@@ -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.
|
entirely suited in real-time and/or interactive settings.
|
||||||
|
|
||||||
`orx-temporal-blur` works well with programs that use `seconds` for their animation input.
|
`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
|
Note that time-step-based simulations or integrations will likely break because your drawing code will be executed multiple times
|
||||||
per frame.
|
per frame.
|
||||||
@@ -39,9 +18,24 @@ per frame.
|
|||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
extend(TemporalBlur()) {
|
extend(TemporalBlur()) {
|
||||||
duration = 0.9
|
duration = 0.9 // duration is in frames
|
||||||
samples = 30
|
samples = 30
|
||||||
fps = 60.0
|
fps = 60.0
|
||||||
jitter = 1.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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
|
sourceSets {
|
||||||
|
demo {
|
||||||
|
java {
|
||||||
|
srcDirs = ["src/demo/kotlin"]
|
||||||
|
compileClasspath += main.getCompileClasspath()
|
||||||
|
runtimeClasspath += main.getRuntimeClasspath()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":orx-noise")
|
implementation project(":orx-noise")
|
||||||
implementation project(":orx-fx")
|
implementation project(":orx-fx")
|
||||||
implementation("org.openrndr:openrndr-filter:$openrndrVersion")
|
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)
|
||||||
}
|
}
|
||||||
22
orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt
Normal file
22
orx-temporal-blur/src/demo/kotlin/DemoBasic01.kt
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt
Normal file
38
orx-temporal-blur/src/demo/kotlin/DemoColorShift01.kt
Normal file
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@ private val add by lazy { PlainAdd() }
|
|||||||
* the scene as many times as you have samples.
|
* the scene as many times as you have samples.
|
||||||
*/
|
*/
|
||||||
class TemporalBlur : Extension {
|
class TemporalBlur : Extension {
|
||||||
|
private var oldClock: () -> Double = { 0.0 }
|
||||||
override var enabled: Boolean = true
|
override var enabled: Boolean = true
|
||||||
|
|
||||||
private var accumulator: RenderTarget? = null
|
private var accumulator: RenderTarget? = null
|
||||||
@@ -51,6 +52,9 @@ class TemporalBlur : Extension {
|
|||||||
private var image: RenderTarget? = null
|
private var image: RenderTarget? = null
|
||||||
private var imageResolved: 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
|
* number of samples to take, more is slower
|
||||||
*/
|
*/
|
||||||
@@ -86,6 +90,12 @@ class TemporalBlur : Extension {
|
|||||||
* */
|
* */
|
||||||
var multisample: BufferMultisample = BufferMultisample.SampleCount(8)
|
var multisample: BufferMultisample = BufferMultisample.SampleCount(8)
|
||||||
|
|
||||||
|
|
||||||
|
var colorMatrix: (Double)->Matrix55 = { Matrix55.IDENTITY }
|
||||||
|
|
||||||
|
|
||||||
|
var beforeDrawAccumulated : TemporalBlur.() -> Unit = {}
|
||||||
|
|
||||||
override fun beforeDraw(drawer: Drawer, program: Program) {
|
override fun beforeDraw(drawer: Drawer, program: Program) {
|
||||||
val extensionOffset = program.extensions.indexOf(this)
|
val extensionOffset = program.extensions.indexOf(this)
|
||||||
val extensionTail = program.extensions.drop(extensionOffset + 1)
|
val extensionTail = program.extensions.drop(extensionOffset + 1)
|
||||||
@@ -155,12 +165,11 @@ class TemporalBlur : Extension {
|
|||||||
drawer.clear(ColorRGBa.BLACK)
|
drawer.clear(ColorRGBa.BLACK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val oldClock = program.clock
|
oldClock = program.clock
|
||||||
val oldClockValue = oldClock()
|
val oldClockValue = oldClock()
|
||||||
|
|
||||||
for (i in samples - 1 downTo 1) {
|
for (i in samples - 1 downTo 1) {
|
||||||
image?.bind()
|
image?.bind()
|
||||||
|
|
||||||
drawer.clear(ColorRGBa.BLACK)
|
drawer.clear(ColorRGBa.BLACK)
|
||||||
program.clock = { oldClockValue - (i * duration) / (fps * samples) }
|
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))
|
add.apply(arrayOf(imageResolved!!.colorBuffer(0), accumulator!!.colorBuffer(0)), accumulator!!.colorBuffer(0))
|
||||||
program.clock = oldClock
|
|
||||||
fsf.setDouble(program, program.clock())
|
fsf.setDouble(program, program.clock())
|
||||||
}
|
}
|
||||||
image?.let {
|
image?.let {
|
||||||
@@ -207,11 +224,23 @@ class TemporalBlur : Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterDraw(drawer: Drawer, program: Program) {
|
override fun afterDraw(drawer: Drawer, program: Program) {
|
||||||
|
// restore clock
|
||||||
|
program.clock = oldClock
|
||||||
// -- we receive one last frame here
|
// -- we receive one last frame here
|
||||||
image?.unbind()
|
image?.unbind()
|
||||||
image!!.colorBuffer(0).copyTo(imageResolved!!.colorBuffer(0))
|
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))
|
add.apply(arrayOf(imageResolved!!.colorBuffer(0), accumulator!!.colorBuffer(0)), accumulator!!.colorBuffer(0))
|
||||||
|
beforeDrawAccumulated()
|
||||||
|
|
||||||
// -- render accumulated result
|
// -- render accumulated result
|
||||||
drawer.isolated {
|
drawer.isolated {
|
||||||
@@ -222,7 +251,7 @@ class TemporalBlur : Extension {
|
|||||||
drawer.drawStyle.blendMode = BlendMode.OVER
|
drawer.drawStyle.blendMode = BlendMode.OVER
|
||||||
|
|
||||||
drawer.clear(ColorRGBa.BLACK)
|
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))
|
drawer.image(accumulator!!.colorBuffer(0))
|
||||||
}
|
}
|
||||||
if (delinearizeOutput) {
|
if (delinearizeOutput) {
|
||||||
|
|||||||
Reference in New Issue
Block a user