diff --git a/orx-fx/build.gradle.kts b/orx-fx/build.gradle.kts index 737cfdef..b2c642f4 100644 --- a/orx-fx/build.gradle.kts +++ b/orx-fx/build.gradle.kts @@ -34,6 +34,8 @@ kotlin { implementation(project(":orx-color")) implementation(project(":orx-fx")) implementation(project(":orx-noise")) + implementation(project(":orx-shapes")) + implementation(project(":orx-image-fit")) } } } diff --git a/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt b/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt index d2e6a83a..01dc766c 100644 --- a/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt +++ b/orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt @@ -37,6 +37,9 @@ class CannyEdgeDetector : Filter1to1( var backgroundOpacity: Double by parameters + @DoubleParameter("fade", 0.0, 1.0, order = 7) + var fade: Double by parameters + init { threshold0 = 2.0 threshold1 = 0.0 @@ -45,6 +48,7 @@ class CannyEdgeDetector : Filter1to1( backgroundColor = ColorRGBa.BLACK backgroundOpacity = 1.0 foregroundOpacity = 1.0 + fade = 1.0 } } diff --git a/orx-fx/src/commonMain/kotlin/edges/Contour.kt b/orx-fx/src/commonMain/kotlin/edges/Contour.kt index 3c65a729..75dece26 100644 --- a/orx-fx/src/commonMain/kotlin/edges/Contour.kt +++ b/orx-fx/src/commonMain/kotlin/edges/Contour.kt @@ -6,10 +6,7 @@ import org.openrndr.color.ColorRGBa import org.openrndr.draw.Filter1to1 import org.openrndr.extra.fx.fx_contour import org.openrndr.extra.fx.mppFilterShader -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.IntParameter +import org.openrndr.extra.parameters.* @Description("Contour") class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) { @@ -28,13 +25,18 @@ class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) { @DoubleParameter("bias", -1.0, 1.0) var bias: Double by parameters - @ColorParameter("contour color") var contourColor: ColorRGBa by parameters @IntParameter("window", 0, 10) var window: Int by parameters + @BooleanParameter("output bands", order = 100) + var outputBands: Boolean by parameters + + @DoubleParameter("fade", 0.0, 1.0, order = 200) + var fade: Double by parameters + init { levels = 6.0 contourWidth = 0.4 @@ -43,5 +45,7 @@ class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) { contourOpacity = 1.0 window = 1 bias = 0.0 + outputBands = false + fade = 1.0 } } diff --git a/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt b/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt new file mode 100644 index 00000000..a653ef8c --- /dev/null +++ b/orx-fx/src/jvmDemo/kotlin/DemoContour01.kt @@ -0,0 +1,56 @@ +/** + * Demonstrate the Contour filter + * @author Edwin Jakobs + */ + +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.createEquivalent +import org.openrndr.draw.loadImage +import org.openrndr.extra.fx.edges.Contour +import org.openrndr.extra.imageFit.imageFit +import org.openrndr.extra.shapes.primitives.grid + +fun main() = application { + configure { + width = 720 + height = 720 + } + program { + val image = loadImage("demo-data/images/image-001.png") + val contour = Contour() + contour.levels = 4.0 + contour.window = 1 + contour.outputBands = true + contour.contourColor = ColorRGBa.PINK + contour.backgroundOpacity = 0.0 + + val edges = image.createEquivalent() + extend { + val cells = drawer.bounds.grid(2, 2).flatten() + val actions = listOf( + { + contour.outputBands = true + contour.levels = 2.0 + }, + { + contour.outputBands = false + contour.levels = 2.0 + }, + { + contour.outputBands = false + contour.levels = 8.0 + }, + { + contour.outputBands = true + contour.levels = 8.0 + }, + ) + for ((cell, action) in cells zip actions) { + action() + contour.apply(image, edges) + drawer.imageFit(edges, cell) + } + } + } +} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag b/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag index 9e9c9db7..6915556c 100644 --- a/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag +++ b/orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag @@ -15,44 +15,46 @@ uniform vec4 foregroundColor; uniform float backgroundOpacity; uniform float foregroundOpacity; +uniform float fade; + vec2 iResolution; -float getAve(vec2 uv){ +float getAve(vec2 uv) { vec3 rgb = texture(tex0, uv).rgb; vec3 lum = vec3(0.299, 0.587, 0.114); return dot(lum, rgb); } // Detect edge. -vec4 sobel(vec2 fragCoord, vec2 dir){ - vec2 uv = fragCoord/iResolution.xy; - vec2 texel = 1./iResolution.xy; - float np = getAve(uv + (vec2(-1,+1) + dir ) * texel * thickness); - float zp = getAve(uv + (vec2( 0,+1) + dir ) * texel * thickness); - float pp = getAve(uv + (vec2(+1,+1) + dir ) * texel * thickness); +vec4 sobel(vec2 fragCoord, vec2 dir) { + vec2 uv = fragCoord / iResolution.xy; + vec2 texel = 1. / iResolution.xy; + float np = getAve(uv + (vec2(-1, + 1) + dir) * texel * thickness); + float zp = getAve(uv + (vec2(0, + 1) + dir) * texel * thickness); + float pp = getAve(uv + (vec2(+ 1, + 1) + dir) * texel * thickness); - float nz = getAve(uv + (vec2(-1, 0) + dir ) * texel * thickness); + float nz = getAve(uv + (vec2(-1, 0) + dir) * texel * thickness); // zz = 0 - float pz = getAve(uv + (vec2(+1, 0) + dir ) * texel * thickness); + float pz = getAve(uv + (vec2(+ 1, 0) + dir) * texel * thickness); - float nn = getAve(uv + (vec2(-1,-1) + dir ) * texel * thickness); - float zn = getAve(uv + (vec2( 0,-1) + dir ) * texel * thickness); - float pn = getAve(uv + (vec2(+1,-1) + dir ) * texel * thickness); + float nn = getAve(uv + (vec2(-1, -1) + dir) * texel * thickness); + float zn = getAve(uv + (vec2(0, -1) + dir) * texel * thickness); + float pn = getAve(uv + (vec2(+ 1, -1) + dir) * texel * thickness); // np zp pp // nz zz pz // nn zn pn #if 0 - float gx = (np*-1. + nz*-2. + nn*-1. + pp*1. + pz*2. + pn*1.); - float gy = (np*-1. + zp*-2. + pp*-1. + nn*1. + zn*2. + pn*1.); + float gx = (np * -1. + nz * -2. + nn * -1. + pp * 1. + pz * 2. + pn * 1.); + float gy = (np * -1. + zp * -2. + pp * -1. + nn * 1. + zn * 2. + pn * 1.); #else // https://www.shadertoy.com/view/Wds3Rl - float gx = (np*-3. + nz*-10. + nn*-3. + pp*3. + pz*10. + pn*3.); - float gy = (np*-3. + zp*-10. + pp*-3. + nn*3. + zn*10. + pn*3.); + float gx = (np * -3. + nz * -10. + nn * -3. + pp * 3. + pz * 10. + pn * 3.); + float gy = (np * -3. + zp * -10. + pp * -3. + nn * 3. + zn * 10. + pn * 3.); #endif - vec2 G = vec2(gx,gy); + vec2 G = vec2(gx, gy); float grad = length(G); @@ -62,49 +64,53 @@ vec4 sobel(vec2 fragCoord, vec2 dir){ } // Make edge thinner. -vec2 hysteresisThr(vec2 fragCoord, float mn, float mx){ +vec2 hysteresisThr(vec2 fragCoord, float mn, float mx) { vec4 edge = sobel(fragCoord, vec2(0)); vec2 dir = vec2(cos(edge.w), sin(edge.w)); - dir *= vec2(-1,1); // rotate 90 degrees. + dir *= vec2(-1, 1); // rotate 90 degrees. vec4 edgep = sobel(fragCoord, dir); vec4 edgen = sobel(fragCoord, -dir); - if(edge.z < edgep.z || edge.z < edgen.z ) edge.z = 0.; + if (edge.z < edgep.z || edge.z < edgen.z) edge.z = 0.; return vec2( - (edge.z > mn) ? edge.z : 0., - (edge.z > mx) ? edge.z : 0. + (edge.z > mn) ? edge.z : 0., + (edge.z > mx) ? edge.z : 0. ); } -float cannyEdge(vec2 fragCoord, float mn, float mx){ +float cannyEdge(vec2 fragCoord, float mn, float mx) { - vec2 np = hysteresisThr(fragCoord + vec2(-1,+1), mn, mx); - vec2 zp = hysteresisThr(fragCoord + vec2( 0,+1), mn, mx); - vec2 pp = hysteresisThr(fragCoord + vec2(+1,+1), mn, mx); + vec2 np = hysteresisThr(fragCoord + vec2(-1, + 1), mn, mx); + vec2 zp = hysteresisThr(fragCoord + vec2(0, + 1), mn, mx); + vec2 pp = hysteresisThr(fragCoord + vec2(+ 1, + 1), mn, mx); vec2 nz = hysteresisThr(fragCoord + vec2(-1, 0), mn, mx); - vec2 zz = hysteresisThr(fragCoord + vec2( 0, 0), mn, mx); - vec2 pz = hysteresisThr(fragCoord + vec2(+1, 0), mn, mx); + vec2 zz = hysteresisThr(fragCoord + vec2(0, 0), mn, mx); + vec2 pz = hysteresisThr(fragCoord + vec2(+ 1, 0), mn, mx); - vec2 nn = hysteresisThr(fragCoord + vec2(-1,-1), mn, mx); - vec2 zn = hysteresisThr(fragCoord + vec2( 0,-1), mn, mx); - vec2 pn = hysteresisThr(fragCoord + vec2(+1,-1), mn, mx); + vec2 nn = hysteresisThr(fragCoord + vec2(-1, -1), mn, mx); + vec2 zn = hysteresisThr(fragCoord + vec2(0, -1), mn, mx); + vec2 pn = hysteresisThr(fragCoord + vec2(+ 1, -1), mn, mx); // np zp pp // nz zz pz // nn zn pn //return min(1., step(1e-3, zz.x) * (zp.y + nz.y + pz.y + zn.y)*8.); //return min(1., step(1e-3, zz.x) * (np.y + pp.y + nn.y + pn.y)*8.); - return min(1., step(1e-2, zz.x*8.) * smoothstep(.0, .3, np.y + zp.y + pp.y + nz.y + pz.y + nn.y + zn.y + pn.y)*8.); + return min(1., step(1e-2, zz.x * 8.) * smoothstep(.0, .3, np.y + zp.y + pp.y + nz.y + pz.y + nn.y + zn.y + pn.y) * 8.); } -void main(){ +void main() { iResolution = vec2(textureSize(tex0, 0)); + vec4 original = texture(tex0, v_texCoord0); vec2 fragCoord = v_texCoord0 * iResolution; float edge = cannyEdge(fragCoord, threshold0, threshold1); - o_output = mix(foregroundColor * foregroundOpacity, backgroundColor * backgroundOpacity, 1.-edge); + o_output = mix(original, + mix(foregroundColor * foregroundOpacity, + backgroundColor * backgroundOpacity, 1. - edge), + fade); } \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/edges/contour.frag b/orx-fx/src/shaders/glsl/edges/contour.frag index 7df091cb..ef54cf33 100644 --- a/orx-fx/src/shaders/glsl/edges/contour.frag +++ b/orx-fx/src/shaders/glsl/edges/contour.frag @@ -8,27 +8,44 @@ uniform vec4 contourColor; uniform float backgroundOpacity; uniform int window; uniform float bias; +uniform bool outputBands; +uniform float fade; -float calc_contour(vec2 uv) { +vec2 calc_contour(vec2 uv) { vec4 box = texture(tex0, uv); float v = sin(3.1415926535 * levels * (dot(vec3(1.0 / 3.0), box.xyz) + bias)); - float level = floor((dot(vec3(1.0 / 3.0), box.xyz) + bias) * levels) / levels; + float level = floor((dot(vec3(1.0 / 3.0), box.xyz) + bias) * levels); float contour = 1.0 - smoothstep(0., contourWidth, 0.5 * abs(v) / fwidth(v)); - return contour; + return vec2(contour, level); } void main() { vec2 step = 1.0 / vec2(textureSize(tex0, 0)); float contour = 0.0; float weight = 0.0; + float level = 0.0; for (int i = -window; i <= window; ++i) { for (int j = -window; j <= window; ++j) { - contour += calc_contour(v_texCoord0 + step / (float(window) + 1.0) * vec2(float(i), float(j))); + vec2 c = calc_contour(v_texCoord0 + step / (float(window) + 1.0) * vec2(float(i), float(j))); + contour += c.x; + level += c.y; weight += 1.0; } } contour /= weight; + vec4 t = texture(tex0, v_texCoord0); - o_output = t * backgroundOpacity * (1.0 - contour) + contour * contourColor * contourOpacity * t.a; + + if (outputBands) { + level /= weight; + + level = 1.0 - max(0.0, fract(level / 2.0) * 2.0); + contour = level; + } + + o_output = mix(t, + t * backgroundOpacity * (1.0 - contour) + contour * contourColor * contourOpacity * clamp(t.a, 0.0, 1.0), + fade); + } \ No newline at end of file