UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

117 lines (115 loc) 4.66 kB
var wgslSsaoPS = ` #include "screenDepthPS" varying uv0: vec2f; uniform uInvResolution: vec2f; uniform uAspect: f32; fn getWFromProjectionMatrix(p: mat4x4f, v: vec3f) -> f32 { return -v.z; } fn getViewSpaceZFromW(p: mat4x4f, w: f32) -> f32 { return -w; } const kLog2LodRate: f32 = 3.0; fn random(w: vec2f) -> f32 { const m: vec3f = vec3f(0.06711056, 0.00583715, 52.9829189); return fract(m.z * fract(dot(w, m.xy))); } fn getFragCoord() -> vec2f { return pcPosition.xy; } fn computeViewSpacePositionFromDepth(uv: vec2f, linearDepth: f32) -> vec3f { return vec3f((0.5 - uv) * vec2f(uniform.uAspect, 1.0) * linearDepth, linearDepth); } fn faceNormal(dpdx: vec3f, dpdy: vec3f) -> vec3f { return normalize(cross(dpdx, dpdy)); } fn computeViewSpaceNormalDeriv(position: vec3f) -> vec3f { return faceNormal(dpdx(position), dpdy(position)); } fn computeViewSpaceNormalDepth(position: vec3f, uv: vec2f) -> vec3f { let uvdx: vec2f = uv + vec2f(uniform.uInvResolution.x, 0.0); let uvdy: vec2f = uv + vec2f(0.0, uniform.uInvResolution.y); let px: vec3f = computeViewSpacePositionFromDepth(uvdx, -getLinearScreenDepth(uvdx)); let py: vec3f = computeViewSpacePositionFromDepth(uvdy, -getLinearScreenDepth(uvdy)); let dpdx: vec3f = px - position; let dpdy: vec3f = py - position; return faceNormal(dpdx, dpdy); } uniform uSampleCount: vec2f; uniform uSpiralTurns: f32; const PI: f32 = 3.14159; fn tapLocation(i: f32, noise: f32) -> vec3f { let offset: f32 = ((2.0 * PI) * 2.4) * noise; let angle: f32 = ((i * uniform.uSampleCount.y) * uniform.uSpiralTurns) * (2.0 * PI) + offset; let radius: f32 = (i + noise + 0.5) * uniform.uSampleCount.y; return vec3f(cos(angle), sin(angle), radius * radius); } fn startPosition(noise: f32) -> vec2f { let angle: f32 = ((2.0 * PI) * 2.4) * noise; return vec2f(cos(angle), sin(angle)); } uniform uAngleIncCosSin: vec2f; fn tapAngleStep() -> mat2x2f { let t: vec2f = uniform.uAngleIncCosSin; return mat2x2f(vec2f(t.x, t.y), vec2f(-t.y, t.x)); } fn tapLocationFast(i: f32, p: vec2f, noise_in: f32) -> vec3f { let radius: f32 = (i + noise_in + 0.5) * uniform.uSampleCount.y; return vec3f(p.x, p.y, radius * radius); } uniform uMaxLevel: f32; uniform uInvRadiusSquared: f32; uniform uMinHorizonAngleSineSquared: f32; uniform uBias: f32; uniform uPeak2: f32; fn computeAmbientOcclusionSAO(occlusion_ptr: ptr<function, f32>, i: f32, ssDiskRadius: f32, uv: vec2f, origin: vec3f, normal: vec3f, tapPosition: vec2f, noise: f32) { let tap: vec3f = tapLocationFast(i, tapPosition, noise); let ssRadius: f32 = max(1.0, tap.z * ssDiskRadius); let uvSamplePos: vec2f = uv + (ssRadius * tap.xy) * uniform.uInvResolution; let level: f32 = clamp(floor(log2(ssRadius)) - kLog2LodRate, 0.0, uniform.uMaxLevel); let occlusionDepth: f32 = -getLinearScreenDepth(uvSamplePos); let p: vec3f = computeViewSpacePositionFromDepth(uvSamplePos, occlusionDepth); let v: vec3f = p - origin; let vv: f32 = dot(v, v); let vn: f32 = dot(v, normal); var w_val: f32 = max(0.0, 1.0 - vv * uniform.uInvRadiusSquared); w_val = w_val * w_val; w_val = w_val * step(vv * uniform.uMinHorizonAngleSineSquared, vn * vn); *occlusion_ptr = *occlusion_ptr + w_val * max(0.0, vn + origin.z * uniform.uBias) / (vv + uniform.uPeak2); } uniform uProjectionScaleRadius: f32; uniform uIntensity: f32; uniform uRandomize: f32; fn scalableAmbientObscurance(uv: vec2f, origin: vec3f, normal: vec3f) -> f32 { let noise: f32 = random(getFragCoord()) + uniform.uRandomize; var tapPosition: vec2f = startPosition(noise); let angleStep: mat2x2f = tapAngleStep(); let ssDiskRadius: f32 = -(uniform.uProjectionScaleRadius / origin.z); var occlusion: f32 = 0.0; for (var i: i32 = 0; i < i32(uniform.uSampleCount.x); i = i + 1) { computeAmbientOcclusionSAO(&occlusion, f32(i), ssDiskRadius, uv, origin, normal, tapPosition, noise); tapPosition = angleStep * tapPosition; } return occlusion; } uniform uPower: f32; @fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput { var output: FragmentOutput; let uv: vec2f = input.uv0; let depth: f32 = -getLinearScreenDepth(input.uv0); let origin: vec3f = computeViewSpacePositionFromDepth(uv, depth); let normal: vec3f = computeViewSpaceNormalDepth(origin, uv); var occlusion: f32 = 0.0; if (uniform.uIntensity > 0.0) { occlusion = scalableAmbientObscurance(uv, origin, normal); } var ao: f32 = max(0.0, 1.0 - occlusion * uniform.uIntensity); ao = pow(ao, uniform.uPower); output.color = vec4f(ao, ao, ao, 1.0); return output; } `; export { wgslSsaoPS as default };