67 lines
2.4 KiB
GLSL
67 lines
2.4 KiB
GLSL
#version 450 core
|
|
#include "common.glsl"
|
|
// Half-resolution ray march through height fog. Each step samples the sun
|
|
// shadow map and canopy cookie, producing physically shadowed light shafts
|
|
// (god rays). Output: rgb = in-scattered light, a = transmittance.
|
|
|
|
in vec2 vUV;
|
|
layout(binding = 0) uniform sampler2D uDepth;
|
|
layout(location = 0) out vec4 fragColor;
|
|
|
|
float phaseHG(float c, float g) {
|
|
float g2 = g * g;
|
|
return (1.0 - g2) / (4.0 * PI * pow(1.0 + g2 - 2.0 * g * c, 1.5));
|
|
}
|
|
|
|
float fogDensity(vec3 p) {
|
|
float h = exp(-max(p.y - uFogParams.y, 0.0) * uFogParams.x);
|
|
vec3 drift = vec3(uWind.x, 0.05, uWind.y) * uTime * 0.35;
|
|
float n = vnoise3(p * 0.16 + drift) * 0.65 + vnoise3(p * 0.45 - drift * 1.7) * 0.35;
|
|
n = mix(1.0, smoothstep(0.2, 0.8, n) * 1.8, uFogParams.z);
|
|
return uFogColor.w * h * n;
|
|
}
|
|
|
|
void main() {
|
|
float d = texture(uDepth, vUV).r;
|
|
vec3 wp = worldFromDepth(vUV, d);
|
|
vec3 ro = uCamPos.xyz;
|
|
vec3 rd = wp - ro;
|
|
float dist = length(rd);
|
|
rd /= dist;
|
|
dist = min(dist, uFogParams2.y);
|
|
|
|
int steps = int(uFogParams2.z);
|
|
float stepLen = dist / float(steps);
|
|
float jitter = ign(gl_FragCoord.xy);
|
|
|
|
float cosT = dot(rd, uSunDir.xyz);
|
|
// Dual lobe: strong forward scattering + a little back/side so shafts
|
|
// still read when looking away from the sun. Normalized: isotropic = 1.
|
|
float phase = (0.75 * phaseHG(cosT, uFogParams.w) + 0.25 * phaseHG(cosT, -0.25)) * 4.0 * PI;
|
|
vec3 sunL = uSunColor.rgb * uSunDir.w * uFogParams2.x * phase;
|
|
vec3 ambL = uFogColor.rgb * uSkyZenith.w;
|
|
|
|
vec3 scatter = vec3(0.0);
|
|
float T = 1.0;
|
|
for (int i = 0; i < steps; ++i) {
|
|
vec3 p = ro + rd * ((float(i) + jitter) * stepLen);
|
|
float dens = fogDensity(p);
|
|
if (dens < 1e-5) continue;
|
|
// Sparse beam mask: most sun scattering happens inside distinct shafts.
|
|
float vis = sunShadowFast(p) * shaftCookie(p);
|
|
vec3 S = (sunL * vis + ambL) * dens;
|
|
for (int l = 0; l < uLightCount.y; ++l) { // warm haze around fires
|
|
vec3 dl = uLightPos[l].xyz - p;
|
|
float r = uLightPos[l].w;
|
|
float att = max(1.0 - dot(dl, dl) / (r * r), 0.0);
|
|
att *= att;
|
|
S += uLightColor[l].rgb * att * att * dens * 0.35;
|
|
}
|
|
float Ts = exp(-dens * stepLen);
|
|
scatter += T * S * (1.0 - Ts) / dens; // energy-conserving step integral
|
|
T *= Ts;
|
|
if (T < 0.01) break;
|
|
}
|
|
fragColor = vec4(scatter, T);
|
|
}
|