|
// This fragment shader is licensed under CC0 1.0 Universal. No rights reserved. |
|
#version 300 es |
|
|
|
precision highp float; |
|
precision highp sampler2D; |
|
|
|
in vec2 uv; |
|
out vec4 out_color; |
|
|
|
uniform vec2 u_resolution; |
|
uniform float u_time; |
|
uniform vec4 u_mouse; |
|
uniform sampler2D u_textures[16]; |
|
|
|
// Texture 0: Base color (use grayscale color for custom character color) |
|
// Texture 1: Normal map |
|
// Texture 2: Emission map |
|
|
|
#define BASE_EMISSION 0.6 |
|
#define EMISSION_STRENGTH 1.2 |
|
#define DYNAMIC_COLOR vec4(1.000, 0.518, 0.000, 1.000) |
|
#define SMOOTH_TO_CEL 20000.0 |
|
#define CEL_BRACKET_1 0.1 |
|
#define CEL_BRACKET_2 0.4 |
|
#define CEL_BRACKET_3 0.6 |
|
#define CEL_LIGHT_PER_BRACKET 0.1 |
|
#define SHOW_LIGHTBULB 1.0 |
|
|
|
vec4 toOklab(vec4 c); |
|
vec4 toSrgb(vec4 c); |
|
|
|
void main(){ |
|
vec2 st = uv * 2.0 - 1.0; |
|
vec4 base = texture(u_textures[0], uv); |
|
vec4 normal = texture(u_textures[1], uv); |
|
vec4 emission = texture(u_textures[2], uv); |
|
|
|
// Colorize base and emission based on dynamic color for grayscale fragments |
|
vec4 baseLab = toOklab(base); |
|
if (abs(baseLab.y) < 0.001 && abs(baseLab.z) < 0.001) { |
|
base *= DYNAMIC_COLOR; |
|
emission *= DYNAMIC_COLOR; |
|
} |
|
|
|
// Increase emission strength |
|
emission = toOklab(emission); |
|
emission.x *= EMISSION_STRENGTH; |
|
emission = toSrgb(emission); |
|
|
|
// Calculate light from normal map |
|
vec2 xy = (normal.xy - 0.5) * 2.0; |
|
vec2 lightXy = normalize(vec2(cos(u_time * 1.0), sin(u_time * 1.0))); |
|
float light = clamp(dot(xy.xy, lightXy.xy), -1.0, 1.0); |
|
|
|
// Do the light |
|
out_color = base * BASE_EMISSION; |
|
out_color += vec4(CEL_LIGHT_PER_BRACKET) * clamp((light - CEL_BRACKET_1) * SMOOTH_TO_CEL, 0.0, 1.0) * normal.a; |
|
out_color += vec4(CEL_LIGHT_PER_BRACKET) * clamp((light - CEL_BRACKET_2) * SMOOTH_TO_CEL, 0.0, 1.0) * normal.a; |
|
out_color += vec4(CEL_LIGHT_PER_BRACKET) * clamp((light - CEL_BRACKET_3) * SMOOTH_TO_CEL, 0.0, 1.0) * normal.a; |
|
out_color *= base.a; |
|
out_color = mix(out_color, emission, emission.a); |
|
out_color += smoothstep(0.13, 0.1, distance(st, lightXy)) * vec4(1) * SHOW_LIGHTBULB; |
|
out_color.a = out_color.a > 0.0 ? 1.0 : 0.0; |
|
} |
|
|
|
// Oklab |
|
vec4 toOklab(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_ = pow(l, 1. / 3.); |
|
float m_ = pow(m, 1. / 3.); |
|
float s_ = pow(s, 1. / 3.); |
|
|
|
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 toSrgb(vec4 c) { |
|
float l_ = c.r + 0.3963377774f * c.g + 0.2158037573f * c.b; |
|
float m_ = c.r - 0.1055613458f * c.g - 0.0638541728f * c.b; |
|
float s_ = c.r - 0.0894841775f * c.g - 1.2914855480f * c.b; |
|
|
|
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 |
|
); |
|
} |