From a18830c8a7c3361de8d9648ad8fd6cf43c69c5dc Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sat, 7 Dec 2019 18:12:36 +0100 Subject: [PATCH] Add film grain and tonemap filters --- orx-fx/src/main/kotlin/grain/FilmGrain.kt | 26 ++++++ .../main/kotlin/tonemap/Uncharted2Tonemap.kt | 15 +++ .../extra/fx/gl3/grain/film-grain.frag | 93 +++++++++++++++++++ .../fx/gl3/tonemap/uncharted2-tonemap.frag | 29 ++++++ 4 files changed, 163 insertions(+) create mode 100644 orx-fx/src/main/kotlin/grain/FilmGrain.kt create mode 100644 orx-fx/src/main/kotlin/tonemap/Uncharted2Tonemap.kt create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/grain/film-grain.frag create mode 100644 orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/tonemap/uncharted2-tonemap.frag diff --git a/orx-fx/src/main/kotlin/grain/FilmGrain.kt b/orx-fx/src/main/kotlin/grain/FilmGrain.kt new file mode 100644 index 00000000..7811662b --- /dev/null +++ b/orx-fx/src/main/kotlin/grain/FilmGrain.kt @@ -0,0 +1,26 @@ +import org.openrndr.draw.Filter +import org.openrndr.draw.Shader +import org.openrndr.extra.fx.filterFragmentCode + +/** + * Film grain filter + */ +class FilmGrain : Filter(Shader.createFromCode(Filter.filterVertexCode, filterFragmentCode("grain/film-grain.frag"))) { + var useColor: Boolean by parameters + var time: Double by parameters; + var grainLiftRatio: Double by parameters + var grainStrength: Double by parameters + var grainRate: Double by parameters + var grainPitch: Double by parameters + var colorLevel: Double by parameters + + init { + useColor = false + grainLiftRatio = 0.5 + grainStrength = 1.0 + grainRate = 1.0 + grainPitch = 1.0 + colorLevel = 1.0 + } + +} \ No newline at end of file diff --git a/orx-fx/src/main/kotlin/tonemap/Uncharted2Tonemap.kt b/orx-fx/src/main/kotlin/tonemap/Uncharted2Tonemap.kt new file mode 100644 index 00000000..36d81962 --- /dev/null +++ b/orx-fx/src/main/kotlin/tonemap/Uncharted2Tonemap.kt @@ -0,0 +1,15 @@ +package org.openrndr.extra.fx.tonemap + +import org.openrndr.draw.Filter +import org.openrndr.draw.Shader +import org.openrndr.extra.fx.filterFragmentCode + +/** + * Uncharted 2 tonemap filter + */ +class Uncharted2Tonemap : Filter(Shader.createFromCode(filterVertexCode, filterFragmentCode("tonemap/uncharted2-tonemap.frag"))) { + var exposureBias by parameters + init { + exposureBias = 2.0 + } +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/grain/film-grain.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/grain/film-grain.frag new file mode 100644 index 00000000..f6240471 --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/grain/film-grain.frag @@ -0,0 +1,93 @@ +// Licensed under the MIT license: +// https://opensource.org/licenses/MIT. + +// Ad[a|o]pted from shader by "noby" https://www.shadertoy.com/view/3sGSWV +#version 330 core + +uniform sampler2D tex0; +in vec2 v_texCoord0; + +uniform bool useColor;// false +uniform float time; +uniform float grainLiftRatio;// = 0.5; +uniform float grainStrength;//= 1.0; +uniform float grainRate;// = 1.0; +// Range: [0.5, 1.0]. +uniform float grainPitch;// = 1.0; + +uniform float colorLevel;// = 1.0; + +out vec4 o_output; + + +// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW. +float hash(vec3 p3){ + p3 = fract(p3 * 0.1031); + p3 += dot(p3, p3.yzx + 19.19); + return fract((p3.x + p3.y) * p3.z); +} + +// From iq: https://www.shadertoy.com/view/4sfGzS. +float noise(vec3 x){ + vec3 i = floor(x); + vec3 f = fract(x); + f = f*f*(3.0-2.0*f); + return mix(mix(mix(hash(i+vec3(0, 0, 0)), + hash(i+vec3(1, 0, 0)), f.x), + mix(hash(i+vec3(0, 1, 0)), + hash(i+vec3(1, 1, 0)), f.x), f.y), + mix(mix(hash(i+vec3(0, 0, 1)), + hash(i+vec3(1, 0, 1)), f.x), + mix(hash(i+vec3(0, 1, 1)), + hash(i+vec3(1, 1, 1)), f.x), f.y), f.z); +} + +// Slightly high-passed continuous value-noise. +float grain_source(vec3 x, float strength, float pitch){ + float center = noise(x); + float v1 = center - noise(vec3(1, 0, 0)/pitch + x) + 0.5; + float v2 = center - noise(vec3(0, 1, 0)/pitch + x) + 0.5; + float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5; + float v4 = center - noise(vec3(0, -1, 0)/pitch + x) + 0.5; + + float total = (v1 + v2 + v3 + v4) / 4.0; + return mix(1.0, 0.5 + total, strength); +} + +void main() { + vec2 uv = v_texCoord0; + vec2 x = gl_FragCoord.xy; + + // Alternatively use iTime here instead and change the grain_rate + // parameter to correspond to frames-per-second. + float t = time; + vec4 colorAlpha = texture(tex0, uv); + vec3 color = colorAlpha.rgb; + vec3 grain = vec3(0); + + if (useColor) { + float rg = grain_source(vec3(x, floor(grainRate*(t))), grainStrength, grainPitch); + float gg = grain_source(vec3(x, floor(grainRate*(t+9.0))), grainStrength, grainPitch); + float bg = grain_source(vec3(x, floor(grainRate*(t-9.0))), grainStrength, grainPitch); + + // Consider using values outside the [0, 1] range as well + // to introduce interesting color shifts to the source. + + vec3 color_grain = vec3(rg, gg, bg); + color_grain = mix(vec3(dot(color_grain, vec3(0.2126, 0.7152, 0.0722))), color_grain, colorLevel); + grain = color_grain; + } else { + const float neutral_grain_factor = sqrt(2.0); + grain = vec3(grain_source(vec3(x, floor(grainRate*t)), grainStrength/neutral_grain_factor, grainPitch)); + } + + // Control whether to add or multiply or lift the source with the grain. + // Multiply (0.0) should be more true to life, but adjust to taste. + + color = max(mix(color*grain, color+(grain-1.0), grainLiftRatio), 0.0); + + // After this you would normally perform tone mapping, + // apply the grain before that. + o_output.rgb = color; + o_output.a = 1.0; +} \ No newline at end of file diff --git a/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/tonemap/uncharted2-tonemap.frag b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/tonemap/uncharted2-tonemap.frag new file mode 100644 index 00000000..23832bf8 --- /dev/null +++ b/orx-fx/src/main/resources/org/openrndr/extra/fx/gl3/tonemap/uncharted2-tonemap.frag @@ -0,0 +1,29 @@ +// ad[a|o]pted from http://filmicworlds.com/blog/filmic-tonemapping-operators/ +#version 330 core + +float A = 0.15; +float B = 0.50; +float C = 0.10; +float D = 0.20; +float E = 0.02; +float F = 0.30; +float W = 11.2; + +uniform sampler2D tex0; +uniform float exposureBias; +in vec2 v_texCoord0; +out vec4 o_output; + +vec3 Uncharted2Tonemap(vec3 x) { + return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; +} + +void main() { + vec3 texColor = texture(tex0,v_texCoord0).rgb; + vec3 curr = Uncharted2Tonemap(exposureBias*texColor); + vec3 whiteScale = 1.0f/Uncharted2Tonemap(vec3(W)); + vec3 color = curr*whiteScale; + + vec3 retColor = pow(color, vec3(1/2.2)); + o_output = vec4(retColor, 1); +} \ No newline at end of file