playcanvas
Version:
PlayCanvas WebGL game engine
4 lines (2 loc) • 5.04 kB
JavaScript
var shadowSoftPS = "\n\n/**\n * Soft directional shadows PCSS - with and without blocker search.\n */\n\nhighp float fractSinRand( const in vec2 uv ) {\n const highp float a = 12.9898, b = 78.233, c = 43758.5453;\n highp float dt = dot(uv.xy, vec2(a, b)), sn = mod(dt, PI);\n return fract(sin(sn) * c);\n}\n\n// struct to hold precomputed constants and current state\nstruct PoissonDiskData {\n float invNumSamples;\n float angleStep;\n float initialAngle;\n float currentRadius;\n float currentAngle;\n};\n\n// prepare the Poisson disk constants and initialize the current state in the struct\nvoid preparePoissonConstants(out PoissonDiskData data, int sampleCount, int numRings, float randomSeed) {\n const float pi2 = 6.28318530718;\n data.invNumSamples = 1.0 / float(sampleCount);\n data.angleStep = pi2 * float(numRings) * data.invNumSamples;\n data.initialAngle = randomSeed * pi2;\n data.currentRadius = data.invNumSamples;\n data.currentAngle = data.initialAngle;\n}\n\n// generate a Poisson sample using the precomputed struct\nvec2 generatePoissonSample(inout PoissonDiskData data) {\n vec2 offset = vec2(cos(data.currentAngle), sin(data.currentAngle)) * pow(data.currentRadius, 0.75);\n data.currentRadius += data.invNumSamples;\n data.currentAngle += data.angleStep;\n return offset;\n}\n\nvoid PCSSFindBlocker(TEXTURE_ACCEPT(shadowMap), out float avgBlockerDepth, out int numBlockers,\n vec2 shadowCoords, float z, int shadowBlockerSamples, float penumbraSize, float invShadowMapSize, float randomSeed) {\n\n PoissonDiskData poissonData;\n preparePoissonConstants(poissonData, shadowBlockerSamples, 11, randomSeed);\n\n float searchWidth = penumbraSize * invShadowMapSize;\n float blockerSum = 0.0;\n numBlockers = 0;\n\n for( int i = 0; i < shadowBlockerSamples; ++i ) {\n vec2 poissonUV = generatePoissonSample(poissonData);\n vec2 sampleUV = shadowCoords + poissonUV * searchWidth;\n float shadowMapDepth = texture2DLod(shadowMap, sampleUV, 0.0).r;\n if ( shadowMapDepth < z ) {\n blockerSum += shadowMapDepth;\n numBlockers++;\n }\n }\n avgBlockerDepth = blockerSum / float(numBlockers);\n}\n\nfloat PCSSFilter(TEXTURE_ACCEPT(shadowMap), vec2 uv, float receiverDepth, int shadowSamples, float filterRadius, float randomSeed) {\n \n PoissonDiskData poissonData;\n preparePoissonConstants(poissonData, shadowSamples, 11, randomSeed);\n\n float sum = 0.0f;\n for ( int i = 0; i < shadowSamples; ++i )\n {\n vec2 poissonUV = generatePoissonSample(poissonData);\n vec2 sampleUV = uv + poissonUV * filterRadius;\n float depth = texture2DLod(shadowMap, sampleUV, 0.0).r;\n sum += step(receiverDepth, depth);\n }\n return sum / float(shadowSamples);\n} \n\nfloat getPenumbra(float dblocker, float dreceiver, float penumbraSize, float penumbraFalloff) {\n float dist = dreceiver - dblocker;\n float penumbra = 1.0 - pow(1.0 - dist, penumbraFalloff);\n return penumbra * penumbraSize;\n}\n\nfloat PCSSDirectional(TEXTURE_ACCEPT(shadowMap), vec3 shadowCoords, vec4 cameraParams, vec4 softShadowParams) {\n\n float receiverDepth = shadowCoords.z;\n float randomSeed = fractSinRand(gl_FragCoord.xy);\n int shadowSamples = int(softShadowParams.x);\n int shadowBlockerSamples = int(softShadowParams.y);\n float penumbraSize = softShadowParams.z;\n float penumbraFalloff = softShadowParams.w;\n\n // normalized inverse shadow map size to preserve the shadow softness regardless of the shadow resolution\n int shadowMapSize = textureSize(shadowMap, 0).x;\n float invShadowMapSize = 1.0 / float(shadowMapSize);\n invShadowMapSize *= float(shadowMapSize) / 2048.0;\n\n float penumbra;\n\n // contact hardening path\n if (shadowBlockerSamples > 0) {\n\n // find average blocker depth\n float avgBlockerDepth = 0.0;\n int numBlockers = 0;\n PCSSFindBlocker(TEXTURE_PASS(shadowMap), avgBlockerDepth, numBlockers, shadowCoords.xy, receiverDepth, shadowBlockerSamples, penumbraSize, invShadowMapSize, randomSeed);\n\n // early out when no blockers are present\n if (numBlockers < 1)\n return 1.0f;\n\n // penumbra size is based on the blocker depth\n penumbra = getPenumbra(avgBlockerDepth, shadowCoords.z, penumbraSize, penumbraFalloff);\n\n } else {\n\n // constant filter size, no contact hardening\n penumbra = penumbraSize;\n }\n\n float filterRadius = penumbra * invShadowMapSize;\n\n // filtering\n return PCSSFilter(TEXTURE_PASS(shadowMap), shadowCoords.xy, receiverDepth, shadowSamples, filterRadius, randomSeed);\n}\n\nfloat getShadowPCSS(TEXTURE_ACCEPT(shadowMap), vec3 shadowCoord, vec4 shadowParams, vec4 cameraParams, vec4 softShadowParams, vec3 lightDir) {\n return PCSSDirectional(TEXTURE_PASS(shadowMap), shadowCoord, cameraParams, softShadowParams);\n}\n\n";
export { shadowSoftPS as default };