1#define_import_path bevy_render::color_operations3#import bevy_render::maths::{PI_2,PI,FRAC_PI_3}5const HUE_GUARD: f32 = 0.0001;7// https://en.wikipedia.org/wiki/SRGB8fn gamma(value: f32) -> f32 {9 if value <= 0.0 {10 return value;11 }12 if value <= 0.04045 {13 return value / 12.92; // linear falloff in dark values14 } else {15 return pow((value + 0.055) / 1.055, 2.4); // gamma curve in other area16 }17}19// https://en.wikipedia.org/wiki/SRGB20fn inverse_gamma(value: f32) -> f32 {21 if value <= 0.0 {22 return value;23 }25 if value <= 0.0031308 {26 return value * 12.92; // linear falloff in dark values27 } else {28 return 1.055 * pow(value, 1.0 / 2.4) - 0.055; // gamma curve in other area29 }30}32fn srgb_to_linear_rgb(color: vec3<f32>) -> vec3<f32> {33 return vec3(34 gamma(color.x),35 gamma(color.y),36 gamma(color.z)37 );38}40fn linear_rgb_to_srgb(color: vec3<f32>) -> vec3<f32> {41 return vec3(42 inverse_gamma(color.x),43 inverse_gamma(color.y),44 inverse_gamma(color.z)45 );46}48fn linear_to_srgb(color: vec3<f32>) -> vec3<f32> {49 return linear_rgb_to_srgb(color);50}52fn srgb_to_linear(color: vec3<f32>) -> vec3<f32> {53 return srgb_to_linear_rgb(color);54}56// https://bottosson.github.io/posts/oklab/57fn oklab_to_linear_rgb(c: vec3<f32>) -> vec3<f32> {58 let l_ = c.x + 0.39633778 * c.y + 0.21580376 * c.z;59 let m_ = c.x - 0.105561346 * c.y - 0.06385417 * c.z;60 let s_ = c.x - 0.08948418 * c.y - 1.2914855 * c.z;61 let l = l_ * l_ * l_;62 let m = m_ * m_ * m_;63 let s = s_ * s_ * s_;64 return vec3(65 4.0767417 * l - 3.3077116 * m + 0.23096994 * s,66 -1.268438 * l + 2.6097574 * m - 0.34131938 * s,67 -0.0041960863 * l - 0.7034186 * m + 1.7076147 * s,68 );69}71// https://bottosson.github.io/posts/oklab/ - inverse of oklab_to_linear_rgb72fn linear_rgb_to_oklab(c: vec3<f32>) -> vec3<f32> {73 let l_ = pow(c.x * 0.4122214708 + c.y * 0.5363325363 + c.z * 0.0514459929, 1.0 / 3.0);74 let m_ = pow(c.x * 0.2119034982 + c.y * 0.6806995451 + c.z * 0.1073969566, 1.0 / 3.0);75 let s_ = pow(c.x * 0.0883024619 + c.y * 0.2817188376 + c.z * 0.6299787005, 1.0 / 3.0);76 return vec3(77 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_,78 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_,79 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_,80 );81}83fn hsl_to_linear_rgb(hsl: vec3<f32>) -> vec3<f32> {84 let h = hsl.x;85 let s = hsl.y;86 let l = hsl.z;87 let c = (1.0 - abs(2.0 * l - 1.0)) * s;88 let hp = h * 6.0;89 let x = c * (1.0 - abs(hp % 2.0 - 1.0));90 var r: f32 = 0.0;91 var g: f32 = 0.0;92 var b: f32 = 0.0;93 if 0.0 <= hp && hp < 1.0 {94 r = c; g = x; b = 0.0;95 } else if 1.0 <= hp && hp < 2.0 {96 r = x; g = c; b = 0.0;97 } else if 2.0 <= hp && hp < 3.0 {98 r = 0.0; g = c; b = x;99 } else if 3.0 <= hp && hp < 4.0 {100 r = 0.0; g = x; b = c;101 } else if 4.0 <= hp && hp < 5.0 {102 r = x; g = 0.0; b = c;103 } else if 5.0 <= hp && hp < 6.0 {104 r = c; g = 0.0; b = x;105 }106 let m = l - 0.5 * c;107 return srgb_to_linear_rgb(vec3(r + m, g + m, b + m));108}110fn hsv_to_linear_rgb(hsva: vec3<f32>) -> vec3<f32> {111 let h = hsva.x * 6.0;112 let s = hsva.y;113 let v = hsva.z;114 let c = v * s;115 let x = c * (1.0 - abs(h % 2.0 - 1.0));116 let m = v - c;117 var r: f32 = 0.0;118 var g: f32 = 0.0;119 var b: f32 = 0.0;120 if 0.0 <= h && h < 1.0 {121 r = c; g = x; b = 0.0;122 } else if 1.0 <= h && h < 2.0 {123 r = x; g = c; b = 0.0;124 } else if 2.0 <= h && h < 3.0 {125 r = 0.0; g = c; b = x;126 } else if 3.0 <= h && h < 4.0 {127 r = 0.0; g = x; b = c;128 } else if 4.0 <= h && h < 5.0 {129 r = x; g = 0.0; b = c;130 } else if 5.0 <= h && h < 6.0 {131 r = c; g = 0.0; b = x;132 }133 return srgb_to_linear_rgb(vec3(r + m, g + m, b + m));134}136fn oklch_to_linear_rgb(c: vec3<f32>) -> vec3<f32> {137 let hue = c.z * PI_2;138 return oklab_to_linear_rgb(vec3(c.x, c.y * cos(hue), c.y * sin(hue)));139}141fn mix_oklch(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {142 // If the chroma is close to zero for one of the endpoints, don't interpolate 143 // the hue and instead use the hue of the other endpoint. This allows gradients that smoothly 144 // transition from black or white to a target color without passing through unrelated hues.145 var h = a.z;146 var g = b.z;147 if a.y < HUE_GUARD {148 h = g;149 } else if b.y < HUE_GUARD {150 g = h;151 }153 let hue_diff = g - h;154 if abs(hue_diff) > 0.5 {155 if hue_diff > 0.0 {156 h += (hue_diff - 1.) * t;157 } else {158 h += (hue_diff + 1.) * t;159 }160 } else {161 h += hue_diff * t;162 }163 return vec3(164 mix(a.x, b.x, t),165 mix(a.y, b.y, t),166 fract(h),167 );168}170fn mix_oklch_long(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {171 var h = a.z;172 var g = b.z;173 if a.y < HUE_GUARD {174 h = g;175 } else if b.y < HUE_GUARD {176 g = h;177 }179 let hue_diff = g - h;180 if abs(hue_diff) < 0.5 {181 if hue_diff >= 0.0 {182 h += (hue_diff - 1.) * t;183 } else {184 h += (hue_diff + 1.) * t;185 }186 } else {187 h += hue_diff * t;188 }189 return vec3(190 mix(a.x, b.x, t),191 mix(a.y, b.y, t),192 fract(h),193 );194}196fn mix_hsl(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {197 // If the saturation is close to zero for one of the endpoints, don't interpolate 198 // the hue and instead use the hue of the other endpoint. This allows gradients that smoothly 199 // transition from black or white to a target color without passing through unrelated hues.200 var h = a.x; 201 var g = b.x;202 if a.y < HUE_GUARD {203 h = g;204 } else if b.y < HUE_GUARD {205 g = h;206 }208 return vec3(209 fract(h + (fract(g - h + 0.5) - 0.5) * t),210 mix(a.y, b.y, t),211 mix(a.z, b.z, t),212 );213}215fn mix_hsl_long(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {216 var h = a.x;217 var g = b.x;218 if a.y < HUE_GUARD {219 h = g;220 } else if b.y < HUE_GUARD {221 g = h;222 }224 let d = fract(g - h + 0.5) - 0.5;225 return vec3(226 fract(h + (d + select(1., -1., 0. < d)) * t),227 mix(a.y, b.y, t),228 mix(a.z, b.z, t),229 );230}232fn mix_hsv(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {233 // If the saturation is close to zero for one of the endpoints, don't interpolate 234 // the hue and instead use the hue of the other endpoint. This allows gradients that smoothly 235 // transition from black or white to a target color without passing through unrelated hues.236 var h = a.x;237 var g = b.x;238 if a.y < HUE_GUARD {239 h = g;240 } else if b.y < HUE_GUARD {241 g = h;242 }244 let hue_diff = g - h;245 if abs(hue_diff) > 0.5 {246 if hue_diff > 0.0 {247 h += (hue_diff - 1.0) * t;248 } else {249 h += (hue_diff + 1.0) * t;250 }251 } else {252 h += hue_diff * t;253 }254 return vec3(255 fract(h),256 mix(a.y, b.y, t),257 mix(a.z, b.z, t),258 );259}261fn mix_hsv_long(a: vec3<f32>, b: vec3<f32>, t: f32) -> vec3<f32> {262 var h = a.x;263 var g = b.x;264 if a.y < HUE_GUARD {265 h = g;266 } else if b.y < HUE_GUARD {267 g = h;268 }270 let hue_diff = g - h;271 if abs(hue_diff) < 0.5 {272 if hue_diff >= 0.0 {273 h += (hue_diff - 1.0) * t;274 } else {275 h += (hue_diff + 1.0) * t;276 }277 } else {278 h += hue_diff * t;279 }280 return vec3(281 fract(h),282 mix(a.y, b.y, t),283 mix(a.z, b.z, t),284 );285}287// Converts HSV to RGB.288//289// Input: H ∈ [0, 2π), S ∈ [0, 1], V ∈ [0, 1].290// Output: R ∈ [0, 1], G ∈ [0, 1], B ∈ [0, 1].291//292// <https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative>293fn hsv_to_rgb(hsv: vec3<f32>) -> vec3<f32> {294 let n = vec3(5.0, 3.0, 1.0);295 let k = (n + hsv.x / FRAC_PI_3) % 6.0;296 return hsv.z - hsv.z * hsv.y * max(vec3(0.0), min(k, min(4.0 - k, vec3(1.0))));297}299// Converts RGB to HSV.300//301// Input: R ∈ [0, 1], G ∈ [0, 1], B ∈ [0, 1].302// Output: H ∈ [0, 2π), S ∈ [0, 1], V ∈ [0, 1].303//304// <https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB>305fn rgb_to_hsv(rgb: vec3<f32>) -> vec3<f32> {306 let x_max = max(rgb.r, max(rgb.g, rgb.b)); // i.e. V307 let x_min = min(rgb.r, min(rgb.g, rgb.b));308 let c = x_max - x_min; // chroma310 var swizzle = vec3<f32>(0.0);311 if (x_max == rgb.r) {312 swizzle = vec3(rgb.gb, 0.0);313 } else if (x_max == rgb.g) {314 swizzle = vec3(rgb.br, 2.0);315 } else {316 swizzle = vec3(rgb.rg, 4.0);317 }319 let h = FRAC_PI_3 * (((swizzle.x - swizzle.y) / c + swizzle.z) % 6.0);321 // Avoid division by zero.322 var s = 0.0;323 if (x_max > 0.0) {324 s = c / x_max;325 }327 return vec3(h, s, x_max);328}