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