Add ZoomBlur, EdgesWork and Sepia filters
This commit is contained in:
@@ -48,6 +48,8 @@ Most blur effects are opacity preserving
|
|||||||
- `BoxBlur`, a simple but fast box blur
|
- `BoxBlur`, a simple but fast box blur
|
||||||
- `GaussianBlur`, a slow but precise Gaussian blur
|
- `GaussianBlur`, a slow but precise Gaussian blur
|
||||||
- `HashBlur`, a noisy blur effect
|
- `HashBlur`, a noisy blur effect
|
||||||
|
- `ZoomBlur`, a directional blur with a zooming effect
|
||||||
|
|
||||||
|
|
||||||
### Color
|
### Color
|
||||||
- `ChromaticAberration`, a chromatic aberration effect based on RGB color separation
|
- `ChromaticAberration`, a chromatic aberration effect based on RGB color separation
|
||||||
@@ -57,6 +59,7 @@ Most blur effects are opacity preserving
|
|||||||
- `LumaMap`, maps luminosity to two colors
|
- `LumaMap`, maps luminosity to two colors
|
||||||
- `LumaOpacity`, maps luminosity to opacity but retains source color
|
- `LumaOpacity`, maps luminosity to opacity but retains source color
|
||||||
- `LumaThreshold`, applies a treshold on the input luminosity and maps to two colors
|
- `LumaThreshold`, applies a treshold on the input luminosity and maps to two colors
|
||||||
|
- `Sepia`, applies a reddish-brown monochrome tint that imitates an old photograph
|
||||||
- `SubtractConstant`, subtract a constant color from the source color
|
- `SubtractConstant`, subtract a constant color from the source color
|
||||||
|
|
||||||
### Distortion
|
### Distortion
|
||||||
@@ -74,6 +77,7 @@ All distortion effects are opacity preserving
|
|||||||
|
|
||||||
### Edges
|
### Edges
|
||||||
- `LumaSobel` - A Sobel-kernel based luminosity edge detector
|
- `LumaSobel` - A Sobel-kernel based luminosity edge detector
|
||||||
|
- `EdgesWork` - An edges filter doubling as erosion
|
||||||
|
|
||||||
### Grain
|
### Grain
|
||||||
- `FilmGrain` - adds film-like grain to the source input
|
- `FilmGrain` - adds film-like grain to the source input
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.openrndr.extra.fx
|
package org.openrndr.extra.fx
|
||||||
|
|
||||||
|
import org.openrndr.draw.ColorFormat
|
||||||
|
import org.openrndr.draw.ColorType
|
||||||
import org.openrndr.resourceUrl
|
import org.openrndr.resourceUrl
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
@@ -8,4 +10,6 @@ internal class FilterTools
|
|||||||
internal fun filterFragmentCode(resourceId: String): String {
|
internal fun filterFragmentCode(resourceId: String): String {
|
||||||
val urlString = resourceUrl("gl3/$resourceId", FilterTools::class.java)
|
val urlString = resourceUrl("gl3/$resourceId", FilterTools::class.java)
|
||||||
return URL(urlString).readText()
|
return URL(urlString).readText()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal data class ColorBufferDescription(val width: Int, val height: Int, val contentScale: Double, val format: ColorFormat, val type: ColorType)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.openrndr.extra.fx.blur
|
|||||||
|
|
||||||
import org.openrndr.draw.*
|
import org.openrndr.draw.*
|
||||||
import org.openrndr.extra.fx.filterFragmentCode
|
import org.openrndr.extra.fx.filterFragmentCode
|
||||||
|
import org.openrndr.extra.fx.ColorBufferDescription
|
||||||
import org.openrndr.extra.parameters.Description
|
import org.openrndr.extra.parameters.Description
|
||||||
import org.openrndr.extra.parameters.DoubleParameter
|
import org.openrndr.extra.parameters.DoubleParameter
|
||||||
import org.openrndr.extra.parameters.IntParameter
|
import org.openrndr.extra.parameters.IntParameter
|
||||||
@@ -14,9 +15,6 @@ import org.openrndr.math.Vector2
|
|||||||
@Description("Approximate Gaussian blur")
|
@Description("Approximate Gaussian blur")
|
||||||
class ApproximateGaussianBlur : Filter(Shader.createFromCode(Filter.filterVertexCode,
|
class ApproximateGaussianBlur : Filter(Shader.createFromCode(Filter.filterVertexCode,
|
||||||
filterFragmentCode("blur/approximate-gaussian-blur.frag"))) {
|
filterFragmentCode("blur/approximate-gaussian-blur.frag"))) {
|
||||||
|
|
||||||
data class ColorBufferDescription(val width: Int, val height: Int, val contentScale: Double, val format: ColorFormat, val type: ColorType)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* blur sample window, default value is 5
|
* blur sample window, default value is 5
|
||||||
*/
|
*/
|
||||||
@@ -43,8 +41,6 @@ class ApproximateGaussianBlur : Filter(Shader.createFromCode(Filter.filterVertex
|
|||||||
|
|
||||||
private var intermediateCache = mutableMapOf<ColorBufferDescription, ColorBuffer>()
|
private var intermediateCache = mutableMapOf<ColorBufferDescription, ColorBuffer>()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
window = 5
|
window = 5
|
||||||
spread = 1.0
|
spread = 1.0
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class BoxBlur : Filter(Shader.createFromCode(Filter.filterVertexCode,
|
|||||||
@DoubleParameter("gain", 0.0, 4.0)
|
@DoubleParameter("gain", 0.0, 4.0)
|
||||||
var gain: Double by parameters
|
var gain: Double by parameters
|
||||||
|
|
||||||
private var intermediateCache = mutableMapOf<ApproximateGaussianBlur.ColorBufferDescription, ColorBuffer>()
|
private var intermediateCache = mutableMapOf<ColorBufferDescription, ColorBuffer>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
window = 5
|
window = 5
|
||||||
@@ -44,7 +44,7 @@ class BoxBlur : Filter(Shader.createFromCode(Filter.filterVertexCode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
||||||
val intermediateDescription = ApproximateGaussianBlur.ColorBufferDescription(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
val intermediateDescription = ColorBufferDescription(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
||||||
val intermediate = intermediateCache.getOrPut(intermediateDescription) {
|
val intermediate = intermediateCache.getOrPut(intermediateDescription) {
|
||||||
colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
||||||
}
|
}
|
||||||
|
|||||||
45
orx-fx/src/main/kotlin/blur/ZoomBlur.kt
Normal file
45
orx-fx/src/main/kotlin/blur/ZoomBlur.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package org.openrndr.extra.fx.blur
|
||||||
|
|
||||||
|
import org.openrndr.draw.ColorBuffer
|
||||||
|
import org.openrndr.draw.Filter
|
||||||
|
import org.openrndr.draw.Shader
|
||||||
|
import org.openrndr.draw.colorBuffer
|
||||||
|
import org.openrndr.extra.fx.filterFragmentCode
|
||||||
|
import org.openrndr.extra.parameters.Description
|
||||||
|
import org.openrndr.extra.parameters.DoubleParameter
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
|
||||||
|
@Description("Zoom Blur")
|
||||||
|
class ZoomBlur : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("blur/zoom-blur.frag"))) {
|
||||||
|
var center: Vector2 by parameters
|
||||||
|
|
||||||
|
@DoubleParameter("strength", 0.0, 1.0)
|
||||||
|
var strength: Double by parameters
|
||||||
|
|
||||||
|
init {
|
||||||
|
center = Vector2.ONE / 2.0
|
||||||
|
strength = 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
private var intermediate: ColorBuffer? = null
|
||||||
|
|
||||||
|
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
||||||
|
intermediate?.let {
|
||||||
|
if (it.width != target[0].width || it.height != target[0].height) {
|
||||||
|
intermediate = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intermediate == null) {
|
||||||
|
intermediate = colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
||||||
|
}
|
||||||
|
|
||||||
|
intermediate?.let {
|
||||||
|
parameters["dimensions"] = Vector2(it.effectiveWidth.toDouble(), it.effectiveHeight.toDouble())
|
||||||
|
|
||||||
|
super.apply(source, arrayOf(it))
|
||||||
|
|
||||||
|
it.copyTo(target[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
orx-fx/src/main/kotlin/color/Sepia.kt
Normal file
17
orx-fx/src/main/kotlin/color/Sepia.kt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package org.openrndr.extra.fx.color
|
||||||
|
|
||||||
|
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("Sepia")
|
||||||
|
class Sepia : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("color/sepia.frag"))) {
|
||||||
|
@DoubleParameter("amount", 0.0, 1.0)
|
||||||
|
var amount: Double by parameters
|
||||||
|
|
||||||
|
init {
|
||||||
|
amount = 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
53
orx-fx/src/main/kotlin/edges/EdgesWork.kt
Normal file
53
orx-fx/src/main/kotlin/edges/EdgesWork.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package org.openrndr.extra.fx.edges
|
||||||
|
|
||||||
|
import org.openrndr.draw.*
|
||||||
|
import org.openrndr.extra.fx.filterFragmentCode
|
||||||
|
import org.openrndr.extra.fx.ColorBufferDescription
|
||||||
|
import org.openrndr.extra.parameters.Description
|
||||||
|
import org.openrndr.extra.parameters.IntParameter
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
|
||||||
|
|
||||||
|
internal class EdgesWork1 : Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("edges/edges-work-1.frag"))) {
|
||||||
|
var delta: Vector2 by parameters
|
||||||
|
|
||||||
|
init {
|
||||||
|
delta = Vector2.ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Description("Edges Work")
|
||||||
|
open class EdgesWork : Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("edges/edges-work-2.frag"))) {
|
||||||
|
/**
|
||||||
|
* radius, default value is 1.0
|
||||||
|
*/
|
||||||
|
@IntParameter("radius", 1, 400)
|
||||||
|
var radius: Int by parameters
|
||||||
|
|
||||||
|
private var delta: Vector2 by parameters
|
||||||
|
|
||||||
|
private val work1 = EdgesWork1()
|
||||||
|
|
||||||
|
private var intermediateCache = mutableMapOf<ColorBufferDescription, ColorBuffer>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
radius = 1
|
||||||
|
delta = Vector2.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
||||||
|
val intermediateDescription = ColorBufferDescription(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
||||||
|
val intermediate = intermediateCache.getOrPut(intermediateDescription) {
|
||||||
|
colorBuffer(target[0].width, target[0].height, target[0].contentScale, target[0].format, target[0].type)
|
||||||
|
}
|
||||||
|
|
||||||
|
intermediate.let {
|
||||||
|
work1.delta = Vector2(radius / it.effectiveWidth.toDouble(), 0.0)
|
||||||
|
work1.apply(source, arrayOf(it))
|
||||||
|
|
||||||
|
parameters["delta"] = Vector2(0.0, radius / it.effectiveHeight.toDouble())
|
||||||
|
super.apply(arrayOf(it), target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import org.openrndr.extra.parameters.ColorParameter
|
|||||||
import org.openrndr.extra.parameters.Description
|
import org.openrndr.extra.parameters.Description
|
||||||
import org.openrndr.extra.parameters.DoubleParameter
|
import org.openrndr.extra.parameters.DoubleParameter
|
||||||
|
|
||||||
@Description("Luma threshold ")
|
@Description("Luma Sobel")
|
||||||
class LumaSobel : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("edges/luma-sobel.frag"))) {
|
class LumaSobel : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("edges/luma-sobel.frag"))) {
|
||||||
|
|
||||||
@ColorParameter("background color")
|
@ColorParameter("background color")
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 v_texCoord0;
|
||||||
|
uniform sampler2D tex0; // input
|
||||||
|
uniform vec2 center;
|
||||||
|
uniform float strength;
|
||||||
|
uniform vec2 dimensions;
|
||||||
|
|
||||||
|
out vec4 o_color;
|
||||||
|
|
||||||
|
float random(vec3 scale, float seed) {
|
||||||
|
/* use the fragment position for a different seed per-pixel */
|
||||||
|
return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation by Evan Wallace (glfx.js)
|
||||||
|
void main() {
|
||||||
|
vec4 color = vec4(0.0);
|
||||||
|
float total = 0.0;
|
||||||
|
vec2 toCenter = center - v_texCoord0;
|
||||||
|
|
||||||
|
/* randomize the lookup values to hide the fixed number of samples */
|
||||||
|
float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);
|
||||||
|
|
||||||
|
for (float t = 0.0; t <= 40.0; t++) {
|
||||||
|
float percent = (t + offset) / 40.0;
|
||||||
|
float weight = 4.0 * (percent - percent * percent);
|
||||||
|
vec4 tex = texture(tex0, v_texCoord0 + toCenter * percent * strength);
|
||||||
|
|
||||||
|
/* switch to pre-multiplied alpha to correctly blur transparent images */
|
||||||
|
tex.rgb *= tex.a;
|
||||||
|
|
||||||
|
color += tex * weight;
|
||||||
|
total += weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
o_color = color / total;
|
||||||
|
|
||||||
|
/* switch back from pre-multiplied alpha */
|
||||||
|
o_color.rgb /= o_color.a + 0.00001;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec2 v_texCoord0;
|
||||||
|
uniform sampler2D tex0; // input
|
||||||
|
uniform float amount;
|
||||||
|
out vec4 o_color;
|
||||||
|
|
||||||
|
// Implementation by Evan Wallace (glfx.js)
|
||||||
|
void main() {
|
||||||
|
vec4 color = texture(tex0, v_texCoord0);
|
||||||
|
float r = color.r;
|
||||||
|
float g = color.g;
|
||||||
|
float b = color.b;
|
||||||
|
|
||||||
|
color.r = min(1.0, (r * (1.0 - (0.607 * amount))) + (g * (0.769 * amount)) + (b * (0.189 * amount)));
|
||||||
|
color.g = min(1.0, (r * 0.349 * amount) + (g * (1.0 - (0.314 * amount))) + (b * 0.168 * amount));
|
||||||
|
color.b = min(1.0, (r * 0.272 * amount) + (g * 0.534 * amount) + (b * (1.0 - (0.869 * amount))));
|
||||||
|
|
||||||
|
o_color = color;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#version 330 core
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
in vec2 v_texCoord0;
|
||||||
|
out vec4 o_color;
|
||||||
|
|
||||||
|
uniform vec2 delta;
|
||||||
|
|
||||||
|
float random(vec3 scale, float seed) {
|
||||||
|
/* use the fragment position for a different seed per-pixel */
|
||||||
|
return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation by Evan Wallace (glfx.js)
|
||||||
|
void main() {
|
||||||
|
vec2 color = vec2(0.0);
|
||||||
|
vec2 total = vec2(0.0);
|
||||||
|
|
||||||
|
/* randomize the lookup values to hide the fixed number of samples */
|
||||||
|
float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);
|
||||||
|
|
||||||
|
for (float t = -30.0; t <= 30.0; t++) {
|
||||||
|
float percent = (t + offset - 0.5) / 30.0;
|
||||||
|
float weight = 1.0 - abs(percent);
|
||||||
|
vec3 tex = texture(tex0, v_texCoord0 + delta * percent).rgb;
|
||||||
|
float average = (tex.r + tex.g + tex.b) / 3.0;
|
||||||
|
color.x += average * weight;
|
||||||
|
total.x += weight;
|
||||||
|
|
||||||
|
if (abs(t) < 15.0) {
|
||||||
|
weight = weight * 2.0 - 1.0;
|
||||||
|
color.y += average * weight;
|
||||||
|
total.y += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o_color = vec4(color / total, 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#version 330 core
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
in vec2 v_texCoord0;
|
||||||
|
out vec4 o_color;
|
||||||
|
|
||||||
|
uniform vec2 delta;
|
||||||
|
uniform int radius;
|
||||||
|
|
||||||
|
float random(vec3 scale, float seed) {
|
||||||
|
/* use the fragment position for a different seed per-pixel */
|
||||||
|
return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation by Evan Wallace (glfx.js)
|
||||||
|
void main() {
|
||||||
|
vec2 color = vec2(0.0);
|
||||||
|
vec2 total = vec2(0.0);
|
||||||
|
|
||||||
|
/* randomize the lookup values to hide the fixed number of samples */
|
||||||
|
float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);
|
||||||
|
|
||||||
|
for (float t = -30.0; t <= 30.0; t++) {
|
||||||
|
float percent = (t + offset - 0.5) / 30.0;
|
||||||
|
float weight = 1.0 - abs(percent);
|
||||||
|
vec2 tex = texture(tex0, v_texCoord0 + delta * percent).xy;
|
||||||
|
color.x += tex.x * weight;
|
||||||
|
total.x += weight;
|
||||||
|
|
||||||
|
if (abs(t) < 15.0) {
|
||||||
|
weight = weight * 2.0 - 1.0;
|
||||||
|
color.y += tex.y * weight;
|
||||||
|
total.y += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float c = clamp(10000.0 * (color.y / total.y - color.x / total.x) + 0.5, 0.0, 1.0);
|
||||||
|
|
||||||
|
o_color = vec4(c, c, c, 1.0);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user