154 lines
4.8 KiB
GLSL
154 lines
4.8 KiB
GLSL
#version 330 core
|
|
in vec2 vUV;
|
|
in float vValue;
|
|
in vec3 vWorldPos;
|
|
out vec4 FragColor;
|
|
|
|
uniform float uMinV;
|
|
uniform float uMaxV;
|
|
uniform sampler2D uDotTex;
|
|
uniform int uHasData; // 0 = no data, 1 = has data
|
|
uniform vec3 uCameraPos;
|
|
uniform float uDotRadius;
|
|
uniform int uRenderMode; // 0=realistic, 1=dataViz
|
|
|
|
const float PI = 3.14159265359;
|
|
|
|
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
|
|
|
vec3 dataColorRamp(float t) {
|
|
t = saturate(t);
|
|
vec3 c0 = vec3(0.10, 0.75, 1.00); // cyan-blue (low)
|
|
vec3 c1 = vec3(0.10, 0.95, 0.35); // green
|
|
vec3 c2 = vec3(1.00, 0.92, 0.22); // yellow
|
|
vec3 c3 = vec3(1.00, 0.22, 0.10); // red (high)
|
|
|
|
if (t < 0.33) return mix(c0, c1, t / 0.33);
|
|
if (t < 0.66) return mix(c1, c2, (t - 0.33) / 0.33);
|
|
return mix(c2, c3, (t - 0.66) / 0.34);
|
|
}
|
|
|
|
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
|
|
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
|
}
|
|
|
|
float D_GGX(float NdotH, float roughness) {
|
|
float a = max(0.04, roughness);
|
|
float alpha = a * a;
|
|
float alpha2 = alpha * alpha;
|
|
float denom = (NdotH * NdotH) * (alpha2 - 1.0) + 1.0;
|
|
return alpha2 / (PI * denom * denom + 1e-7);
|
|
}
|
|
|
|
float G_SchlickGGX(float NdotV, float roughness) {
|
|
float r = roughness + 1.0;
|
|
float k = (r * r) / 8.0;
|
|
return NdotV / (NdotV * (1.0 - k) + k + 1e-7);
|
|
}
|
|
|
|
float G_Smith(float NdotV, float NdotL, float roughness) {
|
|
float ggx1 = G_SchlickGGX(NdotV, roughness);
|
|
float ggx2 = G_SchlickGGX(NdotL, roughness);
|
|
return ggx1 * ggx2;
|
|
}
|
|
|
|
float D_GGX_Aniso(vec3 N, vec3 H, vec3 T, vec3 B, float ax, float ay) {
|
|
float NdotH = saturate(dot(N, H));
|
|
float TdotH = dot(T, H);
|
|
float BdotH = dot(B, H);
|
|
float ax2 = ax * ax;
|
|
float ay2 = ay * ay;
|
|
float denom = (TdotH * TdotH) / (ax2 + 1e-7) + (BdotH * BdotH) / (ay2 + 1e-7) + NdotH * NdotH;
|
|
return 1.0 / (PI * ax * ay * denom * denom + 1e-7);
|
|
}
|
|
|
|
vec3 evalLight(
|
|
vec3 N,
|
|
vec3 V,
|
|
vec3 L,
|
|
vec3 lightColor,
|
|
vec3 baseColor,
|
|
float metallic,
|
|
float roughness,
|
|
float aniso,
|
|
vec3 brushDir
|
|
) {
|
|
float NdotL = saturate(dot(N, L));
|
|
float NdotV = saturate(dot(N, V));
|
|
if (NdotL <= 0.0 || NdotV <= 0.0) return vec3(0.0);
|
|
|
|
vec3 H = normalize(V + L);
|
|
float NdotH = saturate(dot(N, H));
|
|
float VdotH = saturate(dot(V, H));
|
|
|
|
vec3 F0 = mix(vec3(0.04), baseColor, metallic);
|
|
vec3 F = fresnelSchlick(VdotH, F0);
|
|
|
|
float D = D_GGX(NdotH, roughness);
|
|
if (aniso > 0.001) {
|
|
vec3 T = normalize(brushDir - N * dot(brushDir, N));
|
|
vec3 B = normalize(cross(N, T));
|
|
float alpha = max(0.04, roughness);
|
|
float a = alpha * alpha;
|
|
float ax = mix(a, a * 0.30, aniso);
|
|
float ay = mix(a, a * 2.00, aniso);
|
|
D = D_GGX_Aniso(N, H, T, B, ax, ay);
|
|
}
|
|
|
|
float G = G_Smith(NdotV, NdotL, roughness);
|
|
vec3 spec = (D * G * F) / max(4.0 * NdotV * NdotL, 1e-6);
|
|
|
|
vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
|
|
vec3 diff = kD * baseColor / PI;
|
|
|
|
return (diff + spec) * lightColor * NdotL;
|
|
}
|
|
|
|
void main() {
|
|
vec2 p = vUV * 2.0 - 1.0;
|
|
float r = length(p);
|
|
if (r > 1.0) discard;
|
|
float r01 = saturate(r);
|
|
|
|
// Industrial engineering model: simple plated metal pad (brass/gold-ish).
|
|
// When no data, keep a bright gold base. When data is present, render the
|
|
// data color directly (no remaining gold tint), while preserving depth cues.
|
|
vec3 metalBase = vec3(0.98, 0.82, 0.30);
|
|
float value01 = clamp((vValue - uMinV) / max(1e-6, (uMaxV - uMinV)), 0.0, 1.0);
|
|
vec3 dataCol = dataColorRamp(value01);
|
|
|
|
bool hasData = (uHasData != 0);
|
|
vec3 baseColor = hasData ? dataCol : metalBase;
|
|
|
|
// Mostly flat, with a slight bevel near the edge to catch highlights.
|
|
float slope = mix(0.06, 0.28, smoothstep(0.55, 1.0, r01));
|
|
vec3 N = normalize(vec3(p.x * slope, 1.0, p.y * slope));
|
|
vec3 V = normalize(uCameraPos - vWorldPos);
|
|
|
|
float metallic = hasData ? 0.0 : 0.90;
|
|
float roughness = hasData ? 0.78 : ((uRenderMode == 1) ? 0.70 : 0.55);
|
|
|
|
vec3 keyL = normalize(vec3(0.55, 1.00, 0.25));
|
|
vec3 fillL = normalize(vec3(-0.30, 0.70, -0.80));
|
|
vec3 keyC = vec3(1.00, 0.98, 0.95) * 1.8;
|
|
vec3 fillC = vec3(0.85, 0.90, 1.00) * 0.9;
|
|
|
|
vec3 Lo = vec3(0.0);
|
|
Lo += evalLight(N, V, keyL, keyC, baseColor, metallic, roughness, 0.0, vec3(1.0, 0.0, 0.0));
|
|
Lo += evalLight(N, V, fillL, fillC, baseColor, metallic, roughness, 0.0, vec3(1.0, 0.0, 0.0));
|
|
|
|
vec3 F0 = mix(vec3(0.04), baseColor, metallic);
|
|
vec3 ambient = baseColor * 0.10 + F0 * 0.04;
|
|
|
|
float edgeAO = smoothstep(0.88, 1.0, r01);
|
|
float ao = 1.0 - edgeAO * 0.10;
|
|
|
|
// Subtle boundary ring (engineering-model crispness, not a UI outline).
|
|
float ring = smoothstep(0.82, 0.92, r01) - smoothstep(0.92, 1.00, r01);
|
|
|
|
vec3 col = (ambient + Lo) * ao;
|
|
col = mix(col, col * 0.82, ring * 0.35);
|
|
|
|
FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
|
|
}
|