#ifdef fastShadows
float getGIshadow(sampler2DShadow shadowtex, vec2 coord, float z) {
    return shadow2DLod(shadowtex, vec3(coord, z), 2.5).x;
}

float shadowGI(sampler2DShadow shadowtex, in vec3 wPos, in float sigma) {
    float shadow    = 0.0;
    float diff      = mix(0.33, 1.0, scene.lightmap.y);
    #if s_giQuality==0
        float weight    = 1.0/30.0;
        float noise     = ditherGradNoise()*pi;
        vec2 offset     = vec2(cos(noise), sin(noise))*0.33;
        for (int i = 0; i<30; i++) {
            shadow     += getGIshadow(shadowtex, wPos.xy+(bokeh30[i]+offset)*sigma, wPos.z-fract(abs(offset.x)+i%30)*diff*sigma*0.2)*weight;
        }
    #elif s_giQuality==1
        float weight    = 1.0/50.0;
        float noise     = ditherGradNoise()*pi;
        vec2 offset     = vec2(cos(noise), sin(noise))*0.25;
        for (int i = 0; i<50; i++) {
            shadow     += getGIshadow(shadowtex, wPos.xy+(bokeh50[i]+offset)*sigma, wPos.z-fract(abs(offset.x)+i%50)*diff*sigma*0.2)*weight;
        }
    #endif

    return sstep(shadow, 0.0, 0.9);
}

float getSSS(sampler2DShadow shadowtex, in vec3 wPos, in float sigma) {
    float shadow    = 0.0;

    float weight    = 1.0/15.0;
    float noise     = ditherGradNoise()*pi;
    vec2 offset     = vec2(cos(noise), sin(noise))*0.5;
    for (int i = 0; i<15; i++) {
        shadow     += getGIshadow(shadowtex, wPos.xy+(bokeh15[i]+offset)*sigma, wPos.z-fract(abs(offset.x)+i%15)*sigma*0.02)*weight;
    }

    return shadow;
}

vec3 sssGradient(in vec3 scoord, in float distortion) {
    if (pbr.sss>0.01) {
    float falloff   = getSSS(shadowtex1, scoord, 0.0014/distortion);

    vec3 ssscol     = mix(normalize(scene.albedo), scene.albedo, 0.0);
    vec3 color      = mix(pow4(ssscol), pow2(ssscol), pow4(falloff))*pow3(falloff);

    return color*vec3avg(scene.albedo);
    } else { return vec3(0.0); }
}

#else
float getGIshadow(sampler2D shadowtex, vec2 coord, float cd) {
    float diff = mix(2.0, 1.0, scene.lightmap.y);
    return 1.0-saturate((cd-textureLod(shadowtex, coord, 1.5).x*256)*diff-0.06);
}

float shadowGI(in sampler2D shadowtex, in vec3 wPos, in float cd, in float sigma) {
    float shadow    = 0.0;

    #if s_giQuality==0
        float weight    = 1.0/30.0;
        float noise     = ditherGradNoise()*pi;
        vec2 offset     = vec2(cos(noise), sin(noise))*0.33;
        for (int i = 0; i<30; i++) {
            shadow     += getGIshadow(shadowtex, wPos.xy+(bokeh30[i]+offset)*sigma, cd)*weight;
        }
    #elif s_giQuality==1
        float weight    = 1.0/50.0;
        float noise     = ditherGradNoise()*pi;
        vec2 offset     = vec2(cos(noise), sin(noise))*0.25;
        for (int i = 0; i<50; i++) {
            shadow     += getGIshadow(shadowtex, wPos.xy+(bokeh50[i]+offset)*sigma, cd)*weight;
        }
    #endif

    return sstep(shadow, 0.0, 0.9);
}

float getSSSshadow(sampler2D shadowtex, vec2 coord, float cd) {
    return 1.0-saturate((cd-textureLod(shadowtex, coord, 1.5).x*256)*5.0-0.04);
}

float getSSS(sampler2D shadowtex, in vec3 wPos, in float cd, in float sigma) {
    float shadow    = 0.0;

    float weight    = 1.0/15.0;
    float noise     = ditherGradNoise()*pi;
    vec2 offset     = vec2(cos(noise), sin(noise))*0.5;
    for (int i = 0; i<15; i++) {
        shadow     += getSSSshadow(shadowtex, wPos.xy+(bokeh15[i]+offset)*sigma, cd)*weight;
    }

    return shadow;
}

vec3 sssGradient(in vec3 scoord, in float distortion, in float cd) {
    if (pbr.sss>0.01) {
    float falloff   = getSSS(shadowtex1, scoord, cd, 0.002/distortion);

    vec3 ssscol     = mix(normalize(scene.albedo), scene.albedo, 0.0);
    vec3 color      = mix(pow4(ssscol), pow2(ssscol), pow4(falloff))*pow3(falloff);

    return color*vec3avg(scene.albedo);
    } else { return vec3(0.0); }
}

#endif