playcanvas
Version:
PlayCanvas WebGL game engine
94 lines (92 loc) • 4.35 kB
JavaScript
var shadowSoftPS = `
fn fractSinRand(uv: vec2f) -> f32 {
let PI: f32 = 3.141592653589793;
let a: f32 = 12.9898; let b: f32 = 78.233; let c: f32 = 43758.5453;
let dt: f32 = dot(uv.xy, vec2f(a, b));
let sn: f32 = dt % PI;
return fract(sin(sn) * c);
}
struct VogelDiskData {
invNumSamples: f32,
initialAngle: f32,
currentPointId: f32,
}
fn prepareDiskConstants(data: ptr<function, VogelDiskData>, sampleCount: i32, randomSeed: f32) {
let pi2: f32 = 6.28318530718;
data.invNumSamples = 1.0 / f32(sampleCount);
data.initialAngle = randomSeed * pi2;
data.currentPointId = 0.0;
}
fn generateDiskSample(data: ptr<function, VogelDiskData>) -> vec2f {
let GOLDEN_ANGLE: f32 = 2.399963;
let r: f32 = sqrt((data.currentPointId + 0.5) * data.invNumSamples);
let theta: f32 = data.currentPointId * GOLDEN_ANGLE + data.initialAngle;
let offset: vec2f = vec2f(cos(theta), sin(theta)) * pow(r, 1.33);
data.currentPointId = data.currentPointId + 1.0;
return offset;
}
fn PCSSFindBlocker(shadowMap: texture_2d<f32>, shadowMapSampler: sampler, avgBlockerDepth: ptr<function, f32>, numBlockers: ptr<function, i32>,
shadowCoords: vec2f, z: f32, shadowBlockerSamples: i32, penumbraSize: f32, invShadowMapSize: f32, randomSeed: f32) {
var diskData: VogelDiskData;
prepareDiskConstants(&diskData, shadowBlockerSamples, randomSeed);
let searchWidth: f32 = penumbraSize * invShadowMapSize;
var blockerSum: f32 = 0.0;
var numBlockers_local: i32 = 0;
for( var i: i32 = 0; i < shadowBlockerSamples; i = i + 1 ) {
let diskUV: vec2f = generateDiskSample(&diskData);
let sampleUV: vec2f = shadowCoords + diskUV * searchWidth;
let shadowMapDepth: f32 = textureSampleLevel(shadowMap, shadowMapSampler, sampleUV, 0.0).r;
if ( shadowMapDepth < z ) {
blockerSum = blockerSum + shadowMapDepth;
numBlockers_local = numBlockers_local + 1;
}
}
*avgBlockerDepth = blockerSum / f32(numBlockers_local);
*numBlockers = numBlockers_local;
}
fn PCSSFilter(shadowMap: texture_2d<f32>, shadowMapSampler: sampler, uv: vec2f, receiverDepth: f32, shadowSamples: i32, filterRadius: f32, randomSeed: f32) -> f32 {
var diskData: VogelDiskData;
prepareDiskConstants(&diskData, shadowSamples, randomSeed);
var sum: f32 = 0.0;
for (var i: i32 = 0; i < shadowSamples; i = i + 1) {
let offsetUV: vec2f = generateDiskSample(&diskData) * filterRadius;
let depth: f32 = textureSampleLevel(shadowMap, shadowMapSampler, uv + offsetUV, 0.0).r;
sum = sum + step(receiverDepth, depth);
}
return sum / f32(shadowSamples);
}
fn getPenumbra(dblocker: f32, dreceiver: f32, penumbraSize: f32, penumbraFalloff: f32) -> f32 {
let dist: f32 = dreceiver - dblocker;
let penumbra: f32 = 1.0 - pow(1.0 - dist, penumbraFalloff);
return penumbra * penumbraSize;
}
fn PCSSDirectional(shadowMap: texture_2d<f32>, shadowMapSampler: sampler, shadowCoords: vec3f, cameraParams: vec4f, softShadowParams: vec4f) -> f32 {
let receiverDepth: f32 = shadowCoords.z;
let randomSeed: f32 = fractSinRand(pcPosition.xy);
let shadowSamples: i32 = i32(softShadowParams.x);
let shadowBlockerSamples: i32 = i32(softShadowParams.y);
let penumbraSize: f32 = softShadowParams.z;
let penumbraFalloff: f32 = softShadowParams.w;
let shadowMapSize: i32 = i32(textureDimensions(shadowMap, 0).x);
var invShadowMapSize: f32 = 1.0 / f32(shadowMapSize);
invShadowMapSize = invShadowMapSize * (f32(shadowMapSize) / 2048.0);
var penumbra: f32;
if (shadowBlockerSamples > 0) {
var avgBlockerDepth: f32 = 0.0;
var numBlockers: i32 = 0;
PCSSFindBlocker(shadowMap, shadowMapSampler, &avgBlockerDepth, &numBlockers, shadowCoords.xy, receiverDepth, shadowBlockerSamples, penumbraSize, invShadowMapSize, randomSeed);
if (numBlockers < 1) {
return 1.0;
}
penumbra = getPenumbra(avgBlockerDepth, shadowCoords.z, penumbraSize, penumbraFalloff);
} else {
penumbra = penumbraSize;
}
let filterRadius: f32 = penumbra * invShadowMapSize;
return PCSSFilter(shadowMap, shadowMapSampler, shadowCoords.xy, receiverDepth, shadowSamples, filterRadius, randomSeed);
}
fn getShadowPCSS(shadowMap: texture_2d<f32>, shadowMapSampler: sampler, shadowCoord: vec3f, shadowParams: vec4f, cameraParams: vec4f, softShadowParams: vec4f, lightDir: vec3f) -> f32 {
return PCSSDirectional(shadowMap, shadowMapSampler, shadowCoord, cameraParams, softShadowParams);
}
`;
export { shadowSoftPS as default };