diff --git a/Keishiki/shaders/BinaryComposite/BinaryComposite.frag b/Keishiki/shaders/BinaryComposite/BinaryComposite.frag index e2bc28e..e411ecf 100644 --- a/Keishiki/shaders/BinaryComposite/BinaryComposite.frag +++ b/Keishiki/shaders/BinaryComposite/BinaryComposite.frag @@ -1,4 +1,4 @@ -/* Reference: https://drafts.fxtf.org/compositing-1 */ +/* Reference: https://drafts.fxtf.org/compositing-1, https://www.shadertoy.com/view/XdS3RW */ $input v_texcoord0 @@ -9,6 +9,44 @@ uniform vec4 u_mode; SAMPLER2D(s_A, 0); SAMPLER2D(s_B, 1); +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0f, -1.0f / 3.0f, 2.0f / 3.0f, -1.0f); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0f * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0f, 2.0f / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0f - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); +} + +float Overlay(float s, float d) { + return (d < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); +} + +float HardLight(float s, float d) { + return (s < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); +} + +float SoftLight(float s, float d) { + return (s < 0.5) ? d - (1.0 - 2.0 * s) * d * (1.0 - d) + : (d < 0.25) ? d + (2.0 * s - 1.0) * d * ((16.0 * d - 12.0) * d + 4.0) + : d + (2.0 * s - 1.0) * (sqrt(d) - d); +} + +float VividLight(float s, float d) { + return (s < 0.5) ? 1.0 - (1.0 - d) / (2.0 * s) : d / (2.0 * (1.0 - s)); +} + +float PinLight(float s, float d){ + return (2.0 * s - 1.0 > d) ? 2.0 * s - 1.0 : (s < 0.5 * d) ? 2.0 * s : d; +} + void main() { vec2 uv = vec2(v_texcoord0.x, 1.0f - v_texcoord0.y); vec4 _A = texture2D(s_A, uv); @@ -41,68 +79,85 @@ void main() { else if (mode == 5) { // Exclusion blend = _B.xyz + _A.xyz - 2.0f * _B.xyz * _A.xyz; } - else if (mode == 6) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 6) { // Multiply + blend = _A.xyz * _B.xyz; } - else if (mode == 7) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 7) { // Divide + blend = _A.xyz / _B.xyz; } - else if (mode == 8) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 8) { // Lighten + blend = max(_A.xyz, _B.xyz); } - else if (mode == 9) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 9) { // Darken + blend = min(_A.xyz, _B.xyz); } - else if (mode == 10) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 10) { // Lighter Colour -- Why do these two use l1 instead of l2? + blend = (_A.x + _A.y + _A.z > _B.x + _B.y + _B.z) ? _A.xyz : _B.xyz; } - else if (mode == 11) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 11) { // Darker Colour + blend = (_A.x + _A.y + _A.z < _B.x + _B.y + _B.z) ? _A.xyz : _B.xyz; } - else if (mode == 12) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 12) { // Screen + blend = _A.xyz + _B.xyz - _A.xyz * _B.xyz; } - else if (mode == 13) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 13) { // Overlay + blend = vec3(Overlay(_A.x, _B.x), Overlay(_A.y, _B.y), Overlay(_A.z, _B.z)); } - else if (mode == 14) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 14) { // Hard Light + blend = vec3(HardLight(_A.x, _B.x), HardLight(_A.y, _B.y), HardLight(_A.z, _B.z)); } - else if (mode == 15) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 15) { // Soft Light + blend = vec3(SoftLight(_A.x, _B.x), SoftLight(_A.y, _B.y), SoftLight(_A.z, _B.z)); } - else if (mode == 16) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 16) { // Vivid Light + blend = vec3(VividLight(_A.x, _B.x), VividLight(_A.y, _B.y), VividLight(_A.z, _B.z)); } - else if (mode == 17) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 17) { // Linear Light + blend = 2.0f * _A.xyz + _B.xyz - 1.0f; } - else if (mode == 18) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 18) { // Pin Light + blend = vec3(PinLight(_A.x, _B.x), PinLight(_A.y, _B.y), PinLight(_A.z, _B.z)); } - else if (mode == 19) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 19) { // Colour Burn + blend = 1.0f - (1.0f - _B.xyz) / _A.xyz; } - else if (mode == 20) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 20) { // Linear Burn + blend = _A.xyz + _B.xyz - 1.0f; } - else if (mode == 21) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 21) { // Colour Dodge + blend = _B.xyz / (1.0f - _A.xyz); } - else if (mode == 22) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 22) { // Hue + vec3 d = rgb2hsv(_B.xyz); + d.x = rgb2hsv(_A.xyz).x; + blend = hsv2rgb(d); } - else if (mode == 23) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 23) { // Saturation + vec3 d = rgb2hsv(_B.xyz); + d.y = rgb2hsv(_A.xyz).y; + blend = hsv2rgb(d); } - else if (mode == 24) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 24) { // Colour + vec3 s = rgb2hsv(_A.xyz); + s.z = rgb2hsv(_B.xyz).z; + blend = hsv2rgb(s); } - else if (mode == 25) { - gl_FragColor = vec4(50.0f, 0.0f, 0.0f, 1.0f); + else if (mode == 25) { // Luminosity + float dLum = dot(_B.xyz, vec3(0.3, 0.59, 0.11)); + float sLum = dot(_A.xyz, vec3(0.3, 0.59, 0.11)); + float lum = sLum - dLum; + vec3 c = _B.xyz + lum; + float minC = min(min(c.x, c.y), c.z); + float maxC = max(max(c.x, c.y), c.z); + if (minC < 0.0) + blend = sLum + ((c - sLum) * sLum) / (sLum - minC); + else if (maxC > 1.0) + blend = sLum + ((c - sLum) * (1.0 - sLum)) / (maxC - sLum); + else + blend = c; } else { - gl_FragColor = vec4(uv, 0.0f, 1.0f); + blend = vec3(uv, 0.0f); } // Porter-Duff Source Over diff --git a/TODO.md b/TODO.md index 8a8f4e8..773c238 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # NOW ## Chores -- actually write the blending modes (everything is just alpha over right now) - Node groups ## Compositor @@ -16,11 +15,11 @@ - Motion blur ## UI -- Viewport gizmos (Layer-specific & nodes -- can separate into different tools?) -- Not sure how to go about this -- Node loop detection (separate DFS (extra work) or be smart in recursion) - - detect time-sensitive nodes in tree and display on timeline - Key selection in comp panel - Graph editor +- Node loop detection (separate DFS (extra work) or be smart in recursion) + - detect time-sensitive nodes in tree and display on timeline +- Viewport gizmos (Layer-specific & nodes -- can separate into different tools?) -- Not sure how to go about this # Later ## Compositor