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); } `;