playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
85 lines (84 loc) • 3.41 kB
JavaScript
var volumetricFog_default = `
#include "screenDepthPS"
varying uv0: vec2f;
uniform uFogCameraPos: vec3f;
uniform uFogCameraFwd: vec3f;
uniform uFogInvView: mat4x4f;
uniform uFogProjScale: vec2f;
uniform uFogTint: vec3f;
uniform uFogLightColor: vec3f;
uniform uFogLightDir: vec3f;
uniform uFogAmbient: vec3f;
uniform uFogParams: vec4f;
uniform uFogScatterParams: vec4f;
#ifdef FOG_SHADOWS
uniform uFogShadowMatrixPalette: array<mat4x4f, 4>;
uniform uFogShadowCascadeDistances: vec4f;
uniform uFogShadowParams: vec4f;
#ifdef FOG_SHADOW_PCF
var uFogShadowMap: texture_depth_2d;
var uFogShadowMapSampler: sampler_comparison;
#else
var uFogShadowMap: texture_2d<f32>;
var uFogShadowMapSampler: sampler;
#endif
fn sampleFogShadow(worldPos: vec3f, viewDepth: f32) -> f32 {
if (viewDepth >= uniform.uFogShadowParams.w) {
return 1.0;
}
let comparisons: vec4f = step(uniform.uFogShadowCascadeDistances, vec4f(viewDepth));
let cascadeIndex: i32 = i32(min(dot(comparisons, vec4f(1.0)), uniform.uFogShadowParams.x - 1.0));
let shadowCoord: vec3f = (uniform.uFogShadowMatrixPalette[cascadeIndex] * vec4f(worldPos, 1.0)).xyz;
let z: f32 = shadowCoord.z - uniform.uFogShadowParams.y;
#ifdef FOG_SHADOW_PCF
return textureSampleCompareLevel(uFogShadowMap, uFogShadowMapSampler, shadowCoord.xy, z);
#else
return step(z, textureSampleLevel(uFogShadowMap, uFogShadowMapSampler, shadowCoord.xy, 0.0).r);
#endif
}
#endif
fn fogNoise(fragCoord: vec2f) -> f32 {
const magic: vec3f = vec3f(0.06711056, 0.00583715, 52.9829189);
return fract(magic.z * fract(dot(fragCoord, magic.xy)));
}
fn fogPhase(cosTheta: f32, g: f32) -> f32 {
let g2: f32 = g * g;
let denom: f32 = 1.0 + g2 - 2.0 * g * cosTheta;
return (1.0 - g2) / (12.56637 * denom * sqrt(denom));
}
@fragment
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
var output: FragmentOutput;
let ndcUV: vec2f = vec2f(input.uv0.x, 1.0 - input.uv0.y);
let ndc: vec2f = ndcUV * 2.0 - 1.0;
let rayDir: vec3f = normalize((uniform.uFogInvView * vec4f(ndc * uniform.uFogProjScale, -1.0, 0.0)).xyz);
let rayDot: f32 = max(dot(rayDir, uniform.uFogCameraFwd), 0.001);
let rayLength: f32 = min(getLinearScreenDepth(input.uv0) / rayDot, uniform.uFogParams.w);
let stepCount: f32 = uniform.uFogScatterParams.y;
let dt: f32 = rayLength / stepCount;
let noise: f32 = fract(fogNoise(pcPosition.xy) + uniform.uFogScatterParams.z);
let sunLight: vec3f = uniform.uFogLightColor * fogPhase(dot(rayDir, uniform.uFogLightDir), uniform.uFogScatterParams.x);
var inscatter: vec3f = vec3f(0.0);
var transmittance: f32 = 1.0;
for (var i: f32 = 0.0; i < stepCount; i += 1.0) {
let t: f32 = (i + noise) * dt;
let pos: vec3f = uniform.uFogCameraPos + rayDir * t;
let density: f32 = uniform.uFogParams.x * exp(-uniform.uFogParams.z * max(pos.y - uniform.uFogParams.y, 0.0));
var shadow: f32 = 1.0;
#ifdef FOG_SHADOWS
shadow = mix(1.0, sampleFogShadow(pos, t * rayDot), uniform.uFogScatterParams.w);
#endif
let radiance: vec3f = sunLight * shadow + uniform.uFogAmbient;
inscatter += transmittance * uniform.uFogTint * radiance * (density * dt);
transmittance *= exp(-density * dt);
if (transmittance < 0.005) {
break;
}
}
output.color = vec4f(inscatter, transmittance);
return output;
}
`;
export {
volumetricFog_default as default
};