gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
64 lines (49 loc) • 2.56 kB
JavaScript
//#region src/core/shaders/chunks/fragment/head/get-PCF-point-shadow-contribution.ts
/** Helper chunk to get the PCF soft shadow generated by a given {@link core/lights/PointLight.PointLight | PointLight}. */
const getPCFPointShadowContribution = `
fn getPCFPointShadowContribution(index: i32, shadowPosition: vec4f, fragmentPosition: vec2f, depthCubeTexture: texture_depth_cube) -> f32 {
let pointShadow: PointShadowsElement = pointShadows.pointShadowsElements[index];
var visibility = 0.0;
// for point lights, the uniform @vShadowCoord is re-purposed to hold
// the vector from the light to the world-space position of the fragment.
let lightToPosition: vec3f = shadowPosition.xyz;
// For cube shadow maps, depth is stored as radial distance
let distanceToLight: f32 = length(lightToPosition);
if (distanceToLight < pointShadow.cameraNear || distanceToLight > pointShadow.cameraFar) {
return 1.0;
}
// Direction from light to fragment
// bd3D = base direction 3D
let bd3D: vec3f = normalize(lightToPosition);
let size: vec2u = textureDimensions(depthCubeTexture);
let maxSize: f32 = f32(max(size.x, size.y));
let texelSize: f32 = 1.0 / maxSize;
// Hardware PCF with LinearFilter gives us 4-tap filtering per sample
// 5 samples using Vogel disk + IGN = effectively 20 filtered taps with better distribution
let radius: f32 = pointShadow.radius * texelSize;
// Use IGN to rotate sampling pattern per pixel
let phi: f32 = interleavedGradientNoise(fragmentPosition.xy) * PI2;
var dp = (distanceToLight - pointShadow.cameraNear) / (pointShadow.cameraFar - pointShadow.cameraNear);
dp = saturate(dp - pointShadow.bias);
// Build a tangent-space coordinate system for applying offsets
let absDir: vec3f = abs(bd3D);
var tangent: vec3f = select(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), abs(bd3D.y) > 0.999);
tangent = normalize(cross(bd3D, tangent));
let bitangent: vec3f = cross(bd3D, tangent);
let pcfSamples: i32 = pointShadow.pcfSamples;
for(var i: i32 = 0; i < pcfSamples; i++) {
let offset: vec2f = vogelDiskSample(i, pcfSamples, phi);
let offsetDir: vec3f = tangent * offset.x + bitangent * offset.y;
let sampleDir: vec3f = normalize(bd3D + offsetDir * radius);
visibility += textureSampleCompareLevel(
depthCubeTexture,
depthComparisonSampler,
sampleDir,
dp
);
}
visibility /= f32(pcfSamples);
return mix(1.0, visibility, saturate(pointShadow.intensity));
}`;
//#endregion
export { getPCFPointShadowContribution };