From 1a21589fc14f4ccece520a5552cd7d6198516109 Mon Sep 17 00:00:00 2001 From: ChaoticByte Date: Fri, 17 Jan 2025 16:50:34 +0100 Subject: [PATCH] Implement oklab, oklch color space conversion functions, add example, restructure comments in the shaderlib, implements #37 --- examples/oklab.gdshader | 12 +++++ shaderlib/blur.gdshaderinc | 8 ++-- shaderlib/colorspaces.gdshaderinc | 74 ++++++++++++++++++++++++++++-- shaderlib/common.gdshaderinc | 5 ++ shaderlib/denoise.gdshaderinc | 48 +++++++++---------- shaderlib/transform.gdshaderinc | 6 ++- shaderlib/transparency.gdshaderinc | 6 ++- src/Editor.gd | 2 +- 8 files changed, 127 insertions(+), 34 deletions(-) create mode 100644 examples/oklab.gdshader create mode 100644 shaderlib/common.gdshaderinc diff --git a/examples/oklab.gdshader b/examples/oklab.gdshader new file mode 100644 index 0000000..0d8f093 --- /dev/null +++ b/examples/oklab.gdshader @@ -0,0 +1,12 @@ +shader_type canvas_item; + +#include "res://shaderlib/colorspaces.gdshaderinc" + +//!load ./images/swamp.jpg + +void fragment() { + vec4 oklab = rgb2oklab(COLOR); + vec4 oklch = oklab2oklch(oklab); + oklch.z -= 2.0; + COLOR = oklab2rgb(oklch2oklab(oklch)); +} diff --git a/shaderlib/blur.gdshaderinc b/shaderlib/blur.gdshaderinc index 2ee4380..339256d 100644 --- a/shaderlib/blur.gdshaderinc +++ b/shaderlib/blur.gdshaderinc @@ -1,7 +1,9 @@ -// gaussian_blur adapted from https://godotshaders.com/shader/customizable-gausian-blur/ -// original code by https://godotshaders.com/author/djbob-gaming-yt/ -// maximum radius is 64 +/* + gaussian_blur adapted from https://godotshaders.com/shader/customizable-gausian-blur/ + original code by https://godotshaders.com/author/djbob-gaming-yt/ + maximum radius is 64 +*/ vec4 gaussian_blur(sampler2D texture, vec2 uv, int radius, float sigma) { vec2 resolution = 1.0 / vec2(textureSize(texture, 0)); // calculate kernel diff --git a/shaderlib/colorspaces.gdshaderinc b/shaderlib/colorspaces.gdshaderinc index 080884c..6a8ae9b 100644 --- a/shaderlib/colorspaces.gdshaderinc +++ b/shaderlib/colorspaces.gdshaderinc @@ -1,7 +1,16 @@ -// rgb2hsv and hsv2rgb functions adapted -// from https://godotshaders.com/shader/hsv-adjustment/ -// original code by https://godotshaders.com/author/al1-ce/ +/* + Color space conversion functions always work with vec4. + The fourth value is always alpha. +*/ + +#include "res://shaderlib/common.gdshaderinc" + +/* + rgb2hsv and hsv2rgb functions adapted + from https://godotshaders.com/shader/hsv-adjustment/ + original code by https://godotshaders.com/author/al1-ce/ +*/ // Convert RGB to HSV (hue, saturation, brightness) vec4 rgb2hsv(vec4 c) { @@ -20,3 +29,62 @@ vec4 hsv2rgb(vec4 c) { vec3 rgb = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); return vec4(rgb.r, rgb.g, rgb.b, c.a); } + +/* + OkLab and OkLCh + For more details on oklab, see + - https://bottosson.github.io/posts/oklab/ + - https://en.wikipedia.org/wiki/Oklab_color_space +*/ + +vec4 rgb2oklab(vec4 c) { + float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b; + float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b; + float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b; + + float l_ = cbrt(l); + float m_ = cbrt(m); + float s_ = cbrt(s); + + return vec4( + 0.2104542553f*l_ + 0.7936177850f*m_ - 0.0040720468f*s_, + 1.9779984951f*l_ - 2.4285922050f*m_ + 0.4505937099f*s_, + 0.0259040371f*l_ + 0.7827717662f*m_ - 0.8086757660f*s_, + c.a + ); +} + +vec4 oklab2rgb(vec4 c) { + float l_ = c.x + 0.3963377774f * c.y + 0.2158037573f * c.z; + float m_ = c.x - 0.1055613458f * c.y - 0.0638541728f * c.z; + float s_ = c.x - 0.0894841775f * c.y - 1.2914855480f * c.z; + + float l = l_*l_*l_; + float m = m_*m_*m_; + float s = s_*s_*s_; + + return vec4( + +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s, + -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s, + -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s, + c.a + ); +} + +vec4 oklab2oklch(vec4 c) { + return vec4( + c.x, + sqrt((c.y * c.y) + (c.z * c.z)), + atan(c.z, c.y), + c.a + ); +} + +vec4 oklch2oklab(vec4 c) { + return vec4( + c.x, + c.y * cos(c.z), + c.y * sin(c.z), + c.a + ); +} diff --git a/shaderlib/common.gdshaderinc b/shaderlib/common.gdshaderinc new file mode 100644 index 0000000..7ee57f8 --- /dev/null +++ b/shaderlib/common.gdshaderinc @@ -0,0 +1,5 @@ + +// inefficient cuberoot function +float cbrt(float x) { + return pow(x, 1.0/3.0); +} diff --git a/shaderlib/denoise.gdshaderinc b/shaderlib/denoise.gdshaderinc index 322f6ec..585104d 100644 --- a/shaderlib/denoise.gdshaderinc +++ b/shaderlib/denoise.gdshaderinc @@ -1,32 +1,34 @@ -/* glslSmartDenoise by Michele Morrone, adapted -original code: https://github.com/BrutPitt/glslSmartDeNoise -license of the original code: -BSD 2-Clause License +/* + glslSmartDenoise by Michele Morrone, adapted + original code: https://github.com/BrutPitt/glslSmartDeNoise + license of the original code: -Copyright (c) 2019-2020 Michele Morrone -All rights reserved. + BSD 2-Clause License -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: + Copyright (c) 2019-2020 Michele Morrone + All rights reserved. -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI diff --git a/shaderlib/transform.gdshaderinc b/shaderlib/transform.gdshaderinc index c888732..af57cce 100644 --- a/shaderlib/transform.gdshaderinc +++ b/shaderlib/transform.gdshaderinc @@ -1,6 +1,8 @@ -// Load in a texture from a sampler2D with an offset and scale -// See examples/place_texture.gdshader +/* + Load in a texture from a sampler2D with an offset and scale + See examples/place_texture.gdshader +*/ vec4 place_texture(sampler2D sampler, vec2 uv, vec2 texture_pixel_size, vec2 offset, vec2 scale) { vec2 texture_size = vec2(textureSize(sampler, 0)); // position of current pixel; sample color c diff --git a/shaderlib/transparency.gdshaderinc b/shaderlib/transparency.gdshaderinc index 220f0ba..a97e4b7 100644 --- a/shaderlib/transparency.gdshaderinc +++ b/shaderlib/transparency.gdshaderinc @@ -1,6 +1,8 @@ -// Alpha Blending a over b after Bruce A. Wallace -// source: https://en.wikipedia.org/wiki/Alpha_compositing +/* + Alpha Blending a over b after Bruce A. Wallace + source: https://en.wikipedia.org/wiki/Alpha_compositing +*/ vec4 alpha_blend(vec4 b, vec4 a) { float alpha = a.a + (b.a * (1.0 - a.a)); vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha); diff --git a/src/Editor.gd b/src/Editor.gd index 39a2441..4fb716c 100644 --- a/src/Editor.gd +++ b/src/Editor.gd @@ -137,7 +137,7 @@ var shaderlib_regex = { "blur": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/blur\.gdshaderinc\"'), } const shaderlib_functions = { - "colorspaces": ["rgb2hsv", "hsv2rgb"], + "colorspaces": ["rgb2hsv", "hsv2rgb", "oklab2rgb", "rgb2oklab", "oklab2oklch", "oklch2oklab"], "transform": ["place_texture"], "transparency": ["alpha_blend"], "effects": ["pixelate"],