Repository: playcanvas/engine
File: src/scene/shader-lib/chunks/lit/frag/lighting/shadowSoft.js (JavaScript)

Code before:
 *   If USE_POISSON is defined, a Poisson-disk-like spiral is used.
 *   Otherwise, a Vogel-disk spiral is used.
 */

highp float fractSinRand( const in vec2 uv ) {
    const float PI = 3.141592653589793;
    const highp float a = 12.9898, b = 78.233, c = 43758.5453;
    highp float dt = dot(uv.xy, vec2(a, b)), sn = mod(dt, PI);
    return fract(sin(sn) * c);
}

// struct to hold precomputed constants and current state
struct DiskData {
    float invNumSamples;
    float angleStep;
    float initialAngle;
#ifdef USE_POISSON
    float currentRadius;
    float currentAngle;
#else
    float currentPointId;
#endif
};

// prepare the disk constants and initialize the current state in the struct
void prepareDiskConstants(out DiskData data, int sampleCount, int numRings, float randomSeed) {
    const float pi2 = 6.28318530718;
    data.invNumSamples = 1.0 / float(sampleCount);
    data.angleStep = pi2 * float(numRings) * data.invNumSamples;
    data.initialAngle = randomSeed * pi2;
#ifdef USE_POISSON
    data.currentRadius = data.invNumSamples;
    data.currentAngle = data.initialAngle;
#else
    data.currentPointId = 0.0;
#endif
}

#define GOLDEN_ANGLE 2.399963

vec2 generateDiskSample(inout DiskData data) {
#ifdef USE_POISSON
    vec2 offset = vec2(cos(data.currentAngle), sin(data.currentAngle)) * pow(data.currentRadius, 0.75);
    data.currentRadius += data.invNumSamples;
    data.currentAngle += data.angleStep;
    return offset;
#else
    float r = sqrt((data.currentPointId + 0.5) * data.invNumSamples);
    float theta = data.currentPointId * GOLDEN_ANGLE + data.initialAngle;

    vec2 offset = vec2(cos(theta), sin(theta)) * pow(r, 1.33);

Review comment (suggestion):
also, I wonder if that `+ 0.5` is needed here. `currentPointId` could / should use integer math, and not floats.

Code after:
highp float fractSinRand( const in vec2 uv ) {
    const float PI = 3.141592653589793;
    const highp float a = 12.9898, b = 78.233, c = 43758.5453;
    highp float dt = dot(uv.xy, vec2(a, b)), sn = mod(dt, PI);
    return fract(sin(sn) * c);
}

// struct to hold precomputed constants and current state
struct VogelDiskData {
    float invNumSamples;
    float initialAngle;
    float currentPointId;
};

// prepare the Vogel disk constants and initialize the current state in the struct
void prepareDiskConstants(out VogelDiskData data, int sampleCount, int numRings, float randomSeed) {
    const float pi2 = 6.28318530718;
    data.invNumSamples = 1.0 / float(sampleCount);
    data.initialAngle = randomSeed * pi2;
    data.currentPointId = 0.0;
}

#define GOLDEN_ANGLE 2.399963

vec2 generateDiskSample(inout VogelDiskData data) {
    float r = sqrt((data.currentPointId + 0.5) * data.invNumSamples);
    float theta = data.currentPointId * GOLDEN_ANGLE + data.initialAngle;

    vec2 offset = vec2(cos(theta), sin(theta)) * pow(r, 1.33);

    data.currentPointId += 1.0;
    return offset;
}

void PCSSFindBlocker(TEXTURE_ACCEPT(shadowMap), out float avgBlockerDepth, out int numBlockers,
    vec2 shadowCoords, float z, int shadowBlockerSamples, float penumbraSize, float invShadowMapSize, float randomSeed) {

    VogelDiskData diskData;
    prepareDiskConstants(diskData, shadowBlockerSamples, 11, randomSeed);

    float searchWidth = penumbraSize * invShadowMapSize;
    float blockerSum = 0.0;
    numBlockers = 0;

    for( int i = 0; i < shadowBlockerSamples; ++i ) {
        vec2 diskUV = generateDiskSample(diskData);
        vec2 sampleUV = shadowCoords + diskUV * searchWidth;
        float shadowMapDepth = texture2DLod(shadowMap, sampleUV, 0.0).r;
        if ( shadowMapDepth < z ) {
            blockerSum += shadowMapDepth;
            numBlockers++;