From 95eecc150ca778600652c435a79722c1232ec7f5 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sun, 9 Feb 2020 22:50:08 +0100 Subject: [PATCH] Add CMYKHalftone, LumaSobel, DropShadow and segmented waves --- orx-fx/src/main/kotlin/distort/Wave.kt | 11 ++ orx-fx/src/main/kotlin/dither/CMYKHalftone.kt | 27 +++++ orx-fx/src/main/kotlin/edges/LumaSobel.kt | 32 ++++++ orx-fx/src/main/kotlin/shadow/DropShadow.kt | 71 ++++++++++++ .../extra/fx/gl3/distort/horizontal-wave.frag | 18 +++- .../extra/fx/gl3/distort/vertical-wave.frag | 18 +++- .../extra/fx/gl3/dither/cmyk-halftone.frag | 101 ++++++++++++++++++ .../extra/fx/gl3/edges/luma-sobel.frag | 49 +++++++++ .../extra/fx/gl3/shadow/dropshadow-blend.frag | 14 +++ .../extra/fx/gl3/shadow/dropshadow-blur.frag | 32 ++++++ 10 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 orx-fx/src/main/kotlin/dither/CMYKHalftone.kt create mode 100644 orx-fx/src/main/kotlin/edges/LumaSobel.kt create mode 100644 orx-fx/src/main/kotlin/shadow/DropShadow.kt create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/dither/cmyk-halftone.frag create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/edges/luma-sobel.frag create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blend.frag create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blur.frag diff --git a/orx-fx/src/main/kotlin/distort/Wave.kt b/orx-fx/src/main/kotlin/distort/Wave.kt index 069151e9..4379affb 100644 --- a/orx-fx/src/main/kotlin/distort/Wave.kt +++ b/orx-fx/src/main/kotlin/distort/Wave.kt @@ -4,6 +4,7 @@ import org.openrndr.draw.* import org.openrndr.extra.fx.filterFragmentCode import org.openrndr.extra.parameters.Description import org.openrndr.extra.parameters.DoubleParameter +import org.openrndr.extra.parameters.IntParameter @Description("Horizontal wave") class HorizontalWave : Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("distort/horizontal-wave.frag"))) { @@ -16,10 +17,15 @@ class HorizontalWave : Filter(Shader.createFromCode(filterVertexCode, filterFrag @DoubleParameter("phase", -0.5, 0.5) var phase: Double by parameters + @IntParameter("segments", 0, 256) + var segments: Int by parameters + + init { frequency = 1.0 amplitude = 0.1 phase = 0.0 + segments = 0 } var bicubicFiltering = true @@ -43,10 +49,15 @@ class VerticalWave : Filter(Shader.createFromCode(filterVertexCode, filterFragme @DoubleParameter("phase", -0.5, 0.5) var phase: Double by parameters + @IntParameter("segments", 0, 256) + var segments: Int by parameters + + init { frequency = 1.0 amplitude = 0.1 phase = 0.0 + segments = 0 } var bicubicFiltering = true override fun apply(source: Array, target: Array) { diff --git a/orx-fx/src/main/kotlin/dither/CMYKHalftone.kt b/orx-fx/src/main/kotlin/dither/CMYKHalftone.kt new file mode 100644 index 00000000..aae21e69 --- /dev/null +++ b/orx-fx/src/main/kotlin/dither/CMYKHalftone.kt @@ -0,0 +1,27 @@ +package org.openrndr.extra.fx.dither + +import org.openrndr.draw.Filter +import org.openrndr.draw.Shader +import org.openrndr.extra.fx.filterFragmentCode +import org.openrndr.extra.parameters.Description +import org.openrndr.extra.parameters.DoubleParameter + +@Description("CMYK Halftone") +class CMYKHalftone: Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("dither/cmyk-halftone.frag"))) { + + @DoubleParameter("scale", 1.0, 30.0, precision = 4) + var scale: Double by parameters + + @DoubleParameter("dotSize", 1.0, 3.0, precision = 4) + var dotSize: Double by parameters + + + @DoubleParameter("rotation", -180.0, 180.0) + var rotation: Double by parameters + + init { + scale = 3.0 + rotation = 0.0 + dotSize = 1.0 + } +} \ No newline at end of file diff --git a/orx-fx/src/main/kotlin/edges/LumaSobel.kt b/orx-fx/src/main/kotlin/edges/LumaSobel.kt new file mode 100644 index 00000000..3ca68afa --- /dev/null +++ b/orx-fx/src/main/kotlin/edges/LumaSobel.kt @@ -0,0 +1,32 @@ +package org.openrndr.extra.fx.edges + +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.Filter +import org.openrndr.draw.Shader +import org.openrndr.extra.fx.filterFragmentCode +import org.openrndr.extra.parameters.ColorParameter +import org.openrndr.extra.parameters.Description +import org.openrndr.extra.parameters.DoubleParameter + +@Description("Luma threshold ") +class LumaSobel : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("edges/luma-sobel.frag"))) { + + @ColorParameter("background color") + var backgroundColor: ColorRGBa by parameters + + @ColorParameter("edge color") + var edgeColor: ColorRGBa by parameters + + @DoubleParameter("background opacity", 0.0, 1.0) + var backgroundOpacity:Double by parameters + + @DoubleParameter("edge opacity", 0.0, 1.0) + var edgeOpacity:Double by parameters + + init { + backgroundColor = ColorRGBa.BLACK + edgeColor = ColorRGBa.WHITE + edgeOpacity = 1.0 + backgroundOpacity = 1.0 + } +} diff --git a/orx-fx/src/main/kotlin/shadow/DropShadow.kt b/orx-fx/src/main/kotlin/shadow/DropShadow.kt new file mode 100644 index 00000000..d22070e5 --- /dev/null +++ b/orx-fx/src/main/kotlin/shadow/DropShadow.kt @@ -0,0 +1,71 @@ +package org.openrndr.extra.fx.shadow + + +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.* +import org.openrndr.extra.fx.filterFragmentCode +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.math.Vector2 +import org.openrndr.resourceUrl + +private class Blend: Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("shadow/dropshadow-blend.frag"))) { + var shift: Vector2 by parameters +} + +@Description("Drop shadow") +class DropShadow : Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("shadow/dropshadow-blur.frag"))) { + + @IntParameter("blur window", 1, 25) + var window: Int by parameters + var spread: Double by parameters + @DoubleParameter("gain", 0.0, 4.0) + var gain: Double by parameters + + var shift: Vector2 = Vector2.ZERO + @ColorParameter("color") + var color: ColorRGBa by parameters + + private var intermediate: ColorBuffer? = null + private var intermediate2: ColorBuffer? = null + private var b = Blend() + + init { + color = ColorRGBa.BLACK + window = 5 + spread = 1.0 + gain = 1.0 + } + + override fun apply(source: Array, target: Array) { + intermediate?.let { + if (it.width != target[0].width || it.height != target[0].height) { + intermediate = null + } + } + intermediate2?.let { + if (it.width != target[0].width || it.height != target[0].height) { + intermediate2 = null + } + } + if (intermediate == null) { + intermediate = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) + } + if (intermediate2 == null) { + intermediate2 = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type) + } + + intermediate?.let { + parameters["blurDirection"] = Vector2(1.0, 0.0) + super.apply(source, arrayOf(it)) + + parameters["blurDirection"] = Vector2(0.0, 1.0) + super.apply(arrayOf(it), arrayOf(intermediate2!!)) + + b.shift = shift / Vector2(target[0].width * 1.0, target[0].height * 1.0) + b.apply(arrayOf(intermediate2!!, source[0]), target) + } + } +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/horizontal-wave.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/horizontal-wave.frag index 8d22147c..23f94819 100644 --- a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/horizontal-wave.frag +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/horizontal-wave.frag @@ -7,11 +7,25 @@ uniform float amplitude; uniform float frequency; out vec4 o_color; + +uniform int segments; +float truncate(float x, int segments) { + if (segments == 0) { + return x; + } else { + return floor(x*segments) / segments; + } +} + void main() { vec2 uv = v_texCoord0; - uv.x += amplitude * cos(uv.y * 3.1415926535 * frequency + phase * 3.1415926535); + uv.x += amplitude * cos(truncate(uv.y, segments) * 3.1415926535 * frequency + phase * 3.1415926535); if (uv.x >= 0.0 && uv.x < 1.0) { - o_color = texture(tex0, uv); + if (segments == 0) { + o_color = texture(tex0, uv); + } else { + o_color = textureLod(tex0, uv, 0.0); + } } else { o_color = vec4(0.0); } diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/vertical-wave.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/vertical-wave.frag index e11993aa..e1bee811 100644 --- a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/vertical-wave.frag +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/distort/vertical-wave.frag @@ -7,11 +7,25 @@ uniform float amplitude; uniform float frequency; out vec4 o_color; + +uniform int segments; +float truncate(float x, int segments) { + if (segments == 0) { + return x; + } else { + return floor(x*segments) / segments; + } +} + void main() { vec2 uv = v_texCoord0; - uv.y += amplitude * sin(uv.x * 3.1415926535 * frequency + phase * 3.1415926535); + uv.y += amplitude * sin(truncate(uv.x, segments) * 3.1415926535 * frequency + phase * 3.1415926535); if (uv.y >= 0.0 && uv.y < 1.0) { - o_color = texture(tex0, uv); + if (segments == 0) { + o_color = texture(tex0, uv); + } else { + o_color = textureLod(tex0, uv, 0.0); + } } else { o_color = vec4(0.0); } diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/dither/cmyk-halftone.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/dither/cmyk-halftone.frag new file mode 100644 index 00000000..abfdff65 --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/dither/cmyk-halftone.frag @@ -0,0 +1,101 @@ +/* Based on CMYK Halftone by tsone https://www.shadertoy.com/view/Mdf3Dn +*/ +#version 330 core + +uniform float dotSize; + +#define SST 0.888 +#define SSQ 0.288 + +uniform float scale; +uniform float rotation; + +in vec2 v_texCoord0; +uniform sampler2D tex0; + +out vec4 o_color; + +vec4 rgb2cmyki(in vec3 c) +{ + float k = max(max(c.r, c.g), c.b); + return min(vec4(c.rgb / k, k), 1.0); +} + +vec3 cmyki2rgb(in vec4 c) +{ + return c.rgb * c.a; +} +vec3 u(vec4 c) { + if (c.a == 0) { + return vec3(0.0); + } else { + return c.rgb/c.a; + } + +} +vec4 cmyki2rgba(in vec4 cmyk){ + vec4 c = vec4(0.0, 1.0, 1.0, 1.0)*(1.0-cmyk.r); + vec4 m = vec4(1.0, 0.0, 1.0, 1.0)*(1.0-cmyk.g); + vec4 y = vec4(1.0, 1.0, 0.0, 1.0)*(1.0-cmyk.b); + vec4 k = vec4(0.0, 0.0, 0.0, 1.0)*(1.0-cmyk.a); + + vec4 f = c; + f = (1.0-f.a) * m + f.a * vec4(u(f)*u(m),1.0) * m.a + (1.0-m.a) * f; + f = (1.0-f.a) * y + f.a * vec4(u(f)*u(y),1.0) * y.a + (1.0-y.a) * f; + f = (1.0-f.a) * k + f.a * vec4(u(f)*u(k),1.0) * k.a + (1.0-k.a) * f; + return f; +} + + +vec2 px2uv(in vec2 px) +{ + return vec2(px / textureSize(tex0, 0)); +} + +vec2 grid(in vec2 px) +{ + return px - mod(px, scale); +} + +vec4 ss(in vec4 v) +{ + return smoothstep(vec4(SST-SSQ), vec4(SST+SSQ), v); +} + +vec4 halftone(in vec2 fc,in mat2 m) +{ + vec2 smp = (grid(m*fc) + 0.5*scale) * m; + float s = min(length(fc-smp) / (dotSize*0.5*scale), 1.0); + vec3 texc = texture(tex0, px2uv(smp+textureSize(tex0, 0)/2.0)).rgb; + vec4 c = rgb2cmyki(texc); + return c+s; +} + +mat2 rotm(in float r) +{ + float cr = cos(r); + float sr = sin(r); + return mat2( + cr,-sr, + sr,cr + ); +} + +void main() { + vec2 fc = v_texCoord0 * textureSize(tex0, 0) - textureSize(tex0, 0)/2.0; + + mat2 mc = rotm(rotation + radians(15.0)); + mat2 mm = rotm(rotation + radians(75.0)); + mat2 my = rotm(rotation); + mat2 mk = rotm(rotation + radians(45.0)); + + float k = halftone(fc, mk).a; + vec4 c = cmyki2rgba(ss(vec4( + halftone(fc, mc).r, + halftone(fc, mm).g, + halftone(fc, my).b, + halftone(fc, mk).a + ))); + + o_color = c; +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/edges/luma-sobel.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/edges/luma-sobel.frag new file mode 100644 index 00000000..ff425b75 --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/edges/luma-sobel.frag @@ -0,0 +1,49 @@ +#version 330 core + +in vec2 v_texCoord0; + +uniform sampler2D tex0; + +uniform vec4 backgroundColor; +uniform vec4 edgeColor; +uniform float backgroundOpacity; +uniform float edgeOpacity; +out vec4 o_color; + +float step = 1.0; + +float luma(vec4 color){ + vec3 n = color.a == 0.0? vec3(0.0) : color.rgb/color.a; + return dot(n, vec3(1.0/3.0)); +} + +void main() { + vec2 step = 1.0 / textureSize(tex0, 0); + + float tl = luma(texture(tex0, v_texCoord0 + vec2(-step.x, step.y))); + float l = luma(texture(tex0, v_texCoord0 + vec2(-step.x, 0))); + float bl = luma(texture(tex0, v_texCoord0 + vec2(-step.x, -step.y))); + float t = luma(texture(tex0, v_texCoord0 + vec2(0, step.y))); + float b = luma(texture(tex0, v_texCoord0 + vec2(0, -step.y))); + float tr = luma(texture(tex0, v_texCoord0 + vec2(step.x, step.y))); + float r = luma(texture(tex0, v_texCoord0 + vec2(step.x, 0))); + float br = luma(texture(tex0, v_texCoord0 + vec2(step.x, -step.y))); + + // Sobel masks (see http://en.wikipedia.org/wiki/Sobel_operator) + // 1 0 -1 -1 -2 -1 + // X = 2 0 -2 Y = 0 0 0 + // 1 0 -1 1 2 1 + + // You could also use Scharr operator: + // 3 0 -3 3 10 3 + // X = 10 0 -10 Y = 0 0 0 + // 3 0 -3 -3 -10 -3 + + float x = tl + 2.0 * l + bl - tr - 2.0 * r - br; + float y = -tl - 2.0 * t - tr + bl + 2.0 * b + br; + float intensity = sqrt((x*x) + (y*y)) / sqrt(2); + vec4 color = mix(vec4(backgroundColor.rgb, backgroundOpacity), vec4(edgeColor.rgb, edgeOpacity), intensity); + + vec4 a = texture(tex0, v_texCoord0); + o_color = vec4(color.rgb, 1.0) * color.a * a.a; +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blend.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blend.frag new file mode 100644 index 00000000..481fddca --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blend.frag @@ -0,0 +1,14 @@ +#version 330 + +in vec2 v_texCoord0; +uniform sampler2D tex0; +uniform sampler2D tex1; +uniform vec2 shift; + +out vec4 o_color; +void main() { + vec4 a = texture(tex0, v_texCoord0-shift); + vec4 b = texture(tex1, v_texCoord0); + float alpha = min(1,max(0, b.a)); + o_color = a * (1.0-alpha) + b; +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blur.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blur.frag new file mode 100644 index 00000000..a9015061 --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/shadow/dropshadow-blur.frag @@ -0,0 +1,32 @@ +// openrndr - gl3 - box-blur + +#version 330 + +in vec2 v_texCoord0; +uniform sampler2D tex0; +uniform vec2 blurDirection; + +uniform int window; +uniform float sigma; +uniform float gain; +uniform vec4 subtract; +uniform float spread; +uniform vec4 color; +out vec4 o_color; +void main() { + vec2 s = textureSize(tex0, 0).xy; + s = vec2(1.0/s.x, 1.0/s.y); + + int w = window; + + vec4 sum = vec4(0, 0, 0, 0); + float weight = 0; + for (int x = -w; x<= w; ++x) { + float lw = 1.0; + sum += texture(tex0, v_texCoord0 + x * blurDirection * s * spread); + weight += lw; + } + + o_color = (sum / weight) * gain; + o_color.rgb = color.rgb * o_color.a; +} \ No newline at end of file