From be122ff83a07086753ffa22f1917ba43beb7f43f Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Fri, 13 Mar 2020 15:03:17 +0100 Subject: [PATCH] Add OuterGlow --- orx-jumpflood/src/main/kotlin/InnerBevel.kt | 61 +++++++++ orx-jumpflood/src/main/kotlin/JumpFlood.kt | 26 +++- orx-jumpflood/src/main/kotlin/OuterGlow.kt | 76 +++++++++++ .../shaders/gl3/alpha-threshold.frag | 11 ++ .../shaders/gl3/encode-subpixel.frag | 124 ++++++++++++++++++ .../resources/shaders/gl3/fx/inner-bevel.frag | 57 ++++++++ .../resources/shaders/gl3/fx/outer-glow.frag | 31 +++++ .../main/resources/shaders/gl3/jumpflood.frag | 4 +- 8 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 orx-jumpflood/src/main/kotlin/InnerBevel.kt create mode 100644 orx-jumpflood/src/main/kotlin/OuterGlow.kt create mode 100644 orx-jumpflood/src/main/resources/shaders/gl3/alpha-threshold.frag create mode 100644 orx-jumpflood/src/main/resources/shaders/gl3/encode-subpixel.frag create mode 100644 orx-jumpflood/src/main/resources/shaders/gl3/fx/inner-bevel.frag create mode 100644 orx-jumpflood/src/main/resources/shaders/gl3/fx/outer-glow.frag diff --git a/orx-jumpflood/src/main/kotlin/InnerBevel.kt b/orx-jumpflood/src/main/kotlin/InnerBevel.kt new file mode 100644 index 00000000..4bf33aaf --- /dev/null +++ b/orx-jumpflood/src/main/kotlin/InnerBevel.kt @@ -0,0 +1,61 @@ +package org.openrndr.extra.jumpfill + +import org.openrndr.draw.* +import org.openrndr.extra.parameters.Description +import org.openrndr.extra.parameters.DoubleParameter +import org.openrndr.math.Vector2 +import org.openrndr.resourceUrl + +private class InnerBevelFilter : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/fx/inner-bevel.frag"))) { + var angle: Double by parameters + var width: Double by parameters + + var noise:Double by parameters + init { + angle = 0.0 + width = 5.0 + noise = 0.0 + } +} + +@Description("Inner bevel") +class InnerBevel : Filter() { + @DoubleParameter("threshold", 0.0, 1.0) + var threshold = 0.01 + + @DoubleParameter("distance scale", 0.0, 1.0) + var distanceScale = 1.0 + + @DoubleParameter("angle", -180.0, 180.0) + var angle = 0.0 + + @DoubleParameter("width", 0.0, 50.0) + var width = 5.0 + + @DoubleParameter("noise", 0.0, 1.0) + var noise = 0.1 + + private var jumpFlooder: JumpFlooder? = null + private val decodeFilter = PixelDirection() + private val bevelFilter = InnerBevelFilter() + + private var distance: ColorBuffer? = null + + override fun apply(source: Array, target: Array) { + if (jumpFlooder == null) { + jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) + } + if (distance == null) { + distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) + } + val result = jumpFlooder!!.jumpFlood(source[0]) + decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) + decodeFilter.distanceScale = distanceScale + decodeFilter.apply(result, result) + result.copyTo(distance!!) + bevelFilter.angle = angle + bevelFilter.width = width + bevelFilter.noise = noise + bevelFilter.apply(arrayOf(source[0], distance!!), target[0]) + } +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/kotlin/JumpFlood.kt b/orx-jumpflood/src/main/kotlin/JumpFlood.kt index 0aabf112..c59e9522 100644 --- a/orx-jumpflood/src/main/kotlin/JumpFlood.kt +++ b/orx-jumpflood/src/main/kotlin/JumpFlood.kt @@ -13,6 +13,15 @@ import kotlin.math.max import kotlin.math.pow class EncodePoints : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/encode-points.frag"))) + +class EncodeSubpixel : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/encode-subpixel.frag"))) { + var threshold by parameters + init { + threshold = 0.5 + } + +} + class JumpFlood : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/jumpflood.frag"))) { var maxSteps: Int by parameters var step: Int by parameters @@ -49,6 +58,15 @@ class Threshold : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/threshold } } +class AlphaThreshold : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/alpha-threshold.frag"))) { + var threshold: Double by parameters + + init { + threshold = 0.5 + } +} + + private val encodePoints by lazy { persistent { EncodePoints() } } private val pixelDistance by lazy { persistent { PixelDistance() } } private val pixelDirection by lazy { persistent { PixelDirection() } } @@ -56,7 +74,8 @@ private val contourPoints by lazy { persistent { ContourPoints() } } private val threshold by lazy { persistent { Threshold() } } private val passthrough by lazy { persistent { Passthrough() } } -class JumpFlooder(val width: Int, val height: Int, format: ColorFormat = ColorFormat.RGB, type: ColorType = ColorType.FLOAT32) { +class JumpFlooder(val width: Int, val height: Int, format: ColorFormat = ColorFormat.RGB, type: ColorType = ColorType.FLOAT32, + val encodePoints: Filter = EncodePoints()) { private val dimension = max(width, height) private val exp = ceil(Math.log(dimension.toDouble()) / Math.log(2.0)).toInt() @@ -70,8 +89,8 @@ class JumpFlooder(val width: Int, val height: Int, format: ColorFormat = ColorFo val final = colorBuffer(squareDim, squareDim, format = format, type = type) - private val square = colorBuffer(squareDim, squareDim, format = format, type = type).apply { - fill(ColorRGBa.BLACK) + private val square = colorBuffer(squareDim, squareDim, format = ColorFormat.RGBa, type = type).apply { + fill(ColorRGBa.BLACK.opacify(0.0)) } @@ -83,6 +102,7 @@ class JumpFlooder(val width: Int, val height: Int, format: ColorFormat = ColorFo input.copyTo(square) encodePoints.apply(square, coordinates[0]) + jumpFlood.maxSteps = exp for (i in 0 until exp) { jumpFlood.step = i jumpFlood.apply(coordinates[i % 2], coordinates[(i + 1) % 2]) diff --git a/orx-jumpflood/src/main/kotlin/OuterGlow.kt b/orx-jumpflood/src/main/kotlin/OuterGlow.kt new file mode 100644 index 00000000..906905d0 --- /dev/null +++ b/orx-jumpflood/src/main/kotlin/OuterGlow.kt @@ -0,0 +1,76 @@ +package org.openrndr.extra.jumpfill + +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.* +import org.openrndr.extra.parameters.ColorParameter +import org.openrndr.extra.parameters.Description +import org.openrndr.extra.parameters.DoubleParameter +import org.openrndr.math.Vector2 +import org.openrndr.resourceUrl + +private class OuterGlowFilter : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/fx/outer-glow.frag"))) { + var angle: Double by parameters + var width: Double by parameters + + var noise: Double by parameters + var color: ColorRGBa by parameters + + var shape: Double by parameters + var imageOpacity: Double by parameters + + init { + angle = 0.0 + width = 5.0 + noise = 0.0 + shape = 1.0 + imageOpacity = 1.0 + } +} + +@Description("Outer glow") +class OuterGlow : Filter() { + @DoubleParameter("width", 0.0, 50.0) + var width = 5.0 + + @DoubleParameter("noise", 0.0, 1.0) + var noise = 0.1 + + @DoubleParameter("shape", 0.0, 10.0) + var shape = 1.0 + + @DoubleParameter("opacity", 0.0, 1.0) + var opacity = 1.0 + + @DoubleParameter("image opacity", 0.0, 1.0) + var imageOpacity = 1.0 + + + @ColorParameter("color") + var color = ColorRGBa.WHITE + + private var jumpFlooder: JumpFlooder? = null + private val decodeFilter = PixelDirection() + private val glowFilter = OuterGlowFilter() + + private var distance: ColorBuffer? = null + + override fun apply(source: Array, target: Array) { + if (jumpFlooder == null) { + jumpFlooder = JumpFlooder(target[0].width, target[0].height, encodePoints = EncodeSubpixel()) + } + if (distance == null) { + distance = colorBuffer(target[0].width, target[0].height, type = ColorType.FLOAT32) + } + val result = jumpFlooder!!.jumpFlood(source[0]) + decodeFilter.originalSize = Vector2(target[0].width * 1.0, target[0].height * 1.0) + decodeFilter.distanceScale = 1.0 + decodeFilter.apply(result, result) + result.copyTo(distance!!) + glowFilter.color = color.opacify(opacity) + glowFilter.width = width + glowFilter.noise = noise + glowFilter.shape = shape + glowFilter.imageOpacity = imageOpacity + glowFilter.apply(arrayOf(source[0], distance!!), target[0]) + } +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/resources/shaders/gl3/alpha-threshold.frag b/orx-jumpflood/src/main/resources/shaders/gl3/alpha-threshold.frag new file mode 100644 index 00000000..814fd36b --- /dev/null +++ b/orx-jumpflood/src/main/resources/shaders/gl3/alpha-threshold.frag @@ -0,0 +1,11 @@ +#version 330 core + +uniform sampler2D tex0; +in vec2 v_texCoord0; +uniform float threshold; +out vec4 o_color; + +void main() { + float ref = step(threshold , texture(tex0, v_texCoord0).a); + o_color = vec4(ref, ref, ref, 1.0); +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/resources/shaders/gl3/encode-subpixel.frag b/orx-jumpflood/src/main/resources/shaders/gl3/encode-subpixel.frag new file mode 100644 index 00000000..6f439493 --- /dev/null +++ b/orx-jumpflood/src/main/resources/shaders/gl3/encode-subpixel.frag @@ -0,0 +1,124 @@ +#version 330 core + +uniform sampler2D tex0; +in vec2 v_texCoord0; +uniform float threshold; + +out vec4 o_color; + +float zd(float d) { + if (d < 0.0001) { + return 1; + } else { + return d; + } +} + +void main() { + vec2 stepSize = 1.0 / textureSize(tex0, 0); + float ref = step(threshold, texture(tex0, v_texCoord0).a); + + + vec2 o = stepSize/2.0; + float t00 = texture(tex0, v_texCoord0 + o + vec2(0.0, 0.0)).a; + float t10 = texture(tex0, v_texCoord0 + o + vec2(stepSize.x, 0.0)).a; + float t01 = texture(tex0, v_texCoord0 + o + vec2(0.0, stepSize.y)).a; + float t11 = texture(tex0, v_texCoord0 + o + vec2(stepSize.x, stepSize.y)).a; + + int mask = 0; + + if (t00 >= threshold) { + mask += 1; + } + if (t10 >= threshold) { + mask += 2; + } + if (t01 >= threshold) { + mask += 4; + } + if (t11 >= threshold) { + mask += 8; + } + + vec2 offset = vec2(0.0); + if (mask == 1) { + offset.x = 1.0 - (threshold-t10) / zd(t00-t10); + offset.y = 1.0 - ((threshold-t01) / zd(t00-t01)); + offset /= 2; + } + if (mask == 2) { + offset.x = ((threshold-t00) / zd(t10-t00)); + offset.y = 1.0-(threshold-t11) / zd(t10-t11); + offset /= 2; + } + if (mask == 3) { // OK + float dy0 = 1.0 - (threshold - t01) / zd(t00 - t01); + float dy1 = 1.0 - (threshold - t11) / zd(t10 - t11); + offset.y = dy0 + dy1; + offset.x = 1.0; + offset /= 2; + } + if (mask == 4) { // OK + offset.x = 1.0 - (threshold-t11) / zd(t01-t11); + offset.y = (threshold-t00) / zd(t01-t00); + offset /= 2; + } + if (mask == 5) { // OK + float dx0 = 1.0- (threshold - t10) / zd(t00 - t10); + float dx1 = 1.0-(threshold - t11) / zd(t01 - t11); + offset.x = dx0 + dx1; + offset.y = 1.0; + offset /= 2; + } + if (mask == 6 || mask == 9) { + offset = vec2(0.5); + } + if (mask == 7) { // OK + offset.x = 1.0 - (threshold-t11) / zd(t01-t11); + offset.y = 1.0 - (threshold-t11) / zd(t10-t11); + offset /= 2; + } + if (mask == 8) { // OK + offset.x = (threshold-t01) / zd(t11-t01); + offset.y = (threshold-t10) / zd(t11-t10); + offset /= 2; + } + if (mask == 10) { // OK + float dx0 = (threshold - t00) / zd(t10 - t00); + float dx1 = (threshold - t01) / zd(t11 - t01); + offset.x = (dx0 + dx1); + offset.y = 1.0; + offset /= 2; + } + if (mask == 11) { // OK + offset.x = (threshold-t01) / zd(t11-t01); + offset.y = (threshold-t01) / zd(t00-t01); + offset /= 2; + } + if (mask == 12) { // OK + float dy0 = (threshold - t00) / zd(t01 - t00); + float dy1 = (threshold - t10) / zd(t11 - t10); + offset.y = dy0 + dy1; + offset.x = 1.0; + offset /= 2; + } + if (mask == 13) { // OK + offset.x = 1.0 - (threshold-t10) / zd(t00-t10); + offset.y = (threshold-t10) / zd(t11-t10); + offset /= 2; + } + if (mask == 14) { // OK + offset.x = (threshold-t00) / zd(t10-t00); + offset.y = (threshold-t00) / zd(t01-t00); + offset /= 2; + } + + float contour = (mask != 0 && mask != 15)?1.0:0.0; + + //float contour = (mask == 14 || mask == 11 || mask == 7 || mask == 13) ? 1.0 : 0.0; + if (contour > 0.0) { + o_color = vec4(v_texCoord0 + offset*stepSize , ref, 1.0); + } else { + o_color = vec4(-1.0, -1.0, 0.0, 1.0); + } +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/resources/shaders/gl3/fx/inner-bevel.frag b/orx-jumpflood/src/main/resources/shaders/gl3/fx/inner-bevel.frag new file mode 100644 index 00000000..a410a782 --- /dev/null +++ b/orx-jumpflood/src/main/resources/shaders/gl3/fx/inner-bevel.frag @@ -0,0 +1,57 @@ +#version 330 core + +uniform sampler2D tex0; // image +uniform sampler2D tex1; // distance + +uniform float angle; +uniform float width; +uniform float noise; + +in vec2 v_texCoord0; + +out vec4 o_color; +#define HASHSCALE 443.8975 +vec2 hash22(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); + p3 += dot(p3, p3.yzx+19.19); + return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); +} + + +void main() { + float r = radians(angle); + + vec4 color = texture(tex0, v_texCoord0); + + + vec2 step = 1.0 / textureSize(tex0, 0); + + vec2 distance = vec2(0.0); + float totalWeight = 0.0; + for (int j = 0; j < 1; ++j) { + for (int i =0; i < 1; ++i) { + vec2 hn = (hash22(v_texCoord0)-0.5) * noise; + vec2 s = texture(tex1, v_texCoord0 + step * vec2(i,j)).xy + hn*0.0; + distance += s; + totalWeight += 1.0; + } + } + distance /= totalWeight; + + //vec2 distance = texture(tex1, v_texCoord0).xy + hn; + + float d = length(distance); + vec2 n = normalize(distance); + vec2 l = vec2(cos(r), sin(r)); + + float e = smoothstep(0.0, width, d) * smoothstep(width*2.0, width, d); + float o = max(0.0,dot(n, l))*e ; + float o2 = max(0.0,-dot(n, l))*e ; + //o_color = vec4(vec3(o),1.0) * color.a; + + vec3 nc = color.a > 0.0? + color.rgb/color.a : vec3(0.0); + + + o_color = vec4(nc+vec3(o)-vec3(o2),1.0) * color.a; +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/resources/shaders/gl3/fx/outer-glow.frag b/orx-jumpflood/src/main/resources/shaders/gl3/fx/outer-glow.frag new file mode 100644 index 00000000..311663c4 --- /dev/null +++ b/orx-jumpflood/src/main/resources/shaders/gl3/fx/outer-glow.frag @@ -0,0 +1,31 @@ +#version 330 core + +uniform sampler2D tex0; // image +uniform sampler2D tex1; // distance + +uniform float width; +uniform float noise; +uniform vec4 color; +uniform float shape; +uniform float imageOpacity; +in vec2 v_texCoord0; + +out vec4 o_color; +#define HASHSCALE 443.8975 +vec2 hash22(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * HASHSCALE); + p3 += dot(p3, p3.yzx+19.19); + return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y)); +} + +void main() { + vec4 original = texture(tex0, v_texCoord0); + vec2 step = 1.0 / textureSize(tex0, 0); + vec2 distance = texture(tex1, v_texCoord0).rg; + float d = length(distance); + vec2 n = normalize(distance); + + vec2 h = hash22(v_texCoord0)*10.0; + float e = exp(-( pow((d+h.x*noise)*1.0/width, shape)) ); + o_color = original * imageOpacity + (1.0-original.a)* vec4(color.rgb, 1.0) * e * color.a; +} \ No newline at end of file diff --git a/orx-jumpflood/src/main/resources/shaders/gl3/jumpflood.frag b/orx-jumpflood/src/main/resources/shaders/gl3/jumpflood.frag index 14d57cd8..112c48fd 100644 --- a/orx-jumpflood/src/main/resources/shaders/gl3/jumpflood.frag +++ b/orx-jumpflood/src/main/resources/shaders/gl3/jumpflood.frag @@ -8,9 +8,9 @@ uniform int step; out vec4 o_color; void main() { - float stepwidth = 1.0 / pow(2.0, step+1); + float stepwidth = 1.0 / pow(2.0, min(maxSteps, step+1)); - float bestDistance = 9999.0; + float bestDistance = 999999.0; vec2 bestCoord = vec2(-1.0); vec2 bestColor = vec2(-1.0);