diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 11:31:24 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 13:05:48 +0100 |
| commit | 0d4b61993cba37c180ac91e40a0fb362711de8de (patch) | |
| tree | f41dd68420b51766c1ca6fc1ee2c20dc5df830d1 /src/shaders | |
Initial implementation
Diffstat (limited to 'src/shaders')
| -rw-r--r-- | src/shaders/hueShift.ts | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/shaders/hueShift.ts b/src/shaders/hueShift.ts new file mode 100644 index 0000000..1cfe105 --- /dev/null +++ b/src/shaders/hueShift.ts @@ -0,0 +1,133 @@ +export const hueShiftVertexShader = ` +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; + +void main() { + vUv = uv; + vNormal = normalize(normalMatrix * normal); + vPosition = position; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); +} +`; + +export const hueShiftFragmentShader = ` +uniform sampler2D map; +uniform bool hasMap; +uniform vec3 baseColor; +uniform float hueShiftRed; +uniform float hueShiftGreen; +uniform float hueShiftBlue; + +varying vec2 vUv; +varying vec3 vNormal; +varying vec3 vPosition; + +vec3 rgb2hsl(vec3 color) { + float maxC = max(max(color.r, color.g), color.b); + float minC = min(min(color.r, color.g), color.b); + float l = (maxC + minC) / 2.0; + + if (maxC == minC) { + return vec3(0.0, 0.0, l); + } + + float d = maxC - minC; + float s = l > 0.5 ? d / (2.0 - maxC - minC) : d / (maxC + minC); + float h; + + if (maxC == color.r) { + h = (color.g - color.b) / d + (color.g < color.b ? 6.0 : 0.0); + } else if (maxC == color.g) { + h = (color.b - color.r) / d + 2.0; + } else { + h = (color.r - color.g) / d + 4.0; + } + + h /= 6.0; + return vec3(h, s, l); +} + +float hue2rgb(float p, float q, float t) { + if (t < 0.0) t += 1.0; + if (t > 1.0) t -= 1.0; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; +} + +vec3 hsl2rgb(vec3 hsl) { + if (hsl.y == 0.0) { + return vec3(hsl.z); + } + + float q = hsl.z < 0.5 ? hsl.z * (1.0 + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y; + float p = 2.0 * hsl.z - q; + + return vec3( + hue2rgb(p, q, hsl.x + 1.0/3.0), + hue2rgb(p, q, hsl.x), + hue2rgb(p, q, hsl.x - 1.0/3.0) + ); +} + +vec3 applyHueShift(vec3 color) { + // Determine dominant color channel + float maxChannel = max(max(color.r, color.g), color.b); + float threshold = 0.1; + + vec3 hsl = rgb2hsl(color); + float shift = 0.0; + + // Apply shift based on dominant color + if (color.r > color.g + threshold && color.r > color.b + threshold) { + // Red dominant + shift = hueShiftRed; + } else if (color.g > color.r + threshold && color.g > color.b + threshold) { + // Green dominant + shift = hueShiftGreen; + } else if (color.b > color.r + threshold && color.b > color.g + threshold) { + // Blue dominant + shift = hueShiftBlue; + } else { + // Mixed - apply weighted average + float total = color.r + color.g + color.b; + if (total > 0.0) { + shift = (color.r * hueShiftRed + color.g * hueShiftGreen + color.b * hueShiftBlue) / total; + } + } + + hsl.x = mod(hsl.x + shift, 1.0); + return hsl2rgb(hsl); +} + +void main() { + vec3 color; + + if (hasMap) { + color = texture2D(map, vUv).rgb; + } else { + color = baseColor; + } + + // Apply hue shift + color = applyHueShift(color); + + // Bright lighting from multiple directions + vec3 lightDir1 = normalize(vec3(1.0, 1.0, 1.0)); + vec3 lightDir2 = normalize(vec3(-1.0, 0.5, -0.5)); + vec3 lightDir3 = normalize(vec3(0.0, -1.0, 0.0)); + + float diff1 = max(dot(vNormal, lightDir1), 0.0); + float diff2 = max(dot(vNormal, lightDir2), 0.0) * 0.5; + float diff3 = max(dot(vNormal, lightDir3), 0.0) * 0.3; + + float ambient = 0.7; + float diffuse = diff1 + diff2 + diff3; + + vec3 finalColor = color * (ambient + diffuse * 0.5); + + gl_FragColor = vec4(finalColor, 1.0); +} +`; |