LYGIA Shader Library

ssao (lygia/lighting/ssao)

ScreenSpace Ambient Occlusion

Dependencies:

Use:

<float> ssao(<SAMPLER_TYPE> texPosition, <SAMPLER_TYPE> texNormal, vec2 <st> [, <float> radius, float <bias>])

Check it on Github



#ifndef SSAO_SAMPLES_NUM
#define SSAO_SAMPLES_NUM 8
#endif

#ifndef SSAO_SAMPLES_ARRAY
#if defined(GLSLVIEWER)
#define SSAO_SAMPLES_ARRAY u_ssaoSamples
uniform vec3 u_ssaoSamples[SSAO_SAMPLES_NUM];
#endif
#endif

#ifndef SSAO_NOISE_NUM
#define SSAO_NOISE_NUM 4
#endif

#ifndef SSAO_NOISE_ARRAY
#if defined(GLSLVIEWER)
#define SSAO_NOISE_ARRAY u_ssaoNoise
uniform vec3 u_ssaoNoise[SSAO_NOISE_NUM];
#endif
#endif

#ifndef CAMERA_PROJECTION_MATRIX
#if defined(GLSLVIEWER)
#define CAMERA_PROJECTION_MATRIX u_projectionMatrix
#else
#define CAMERA_PROJECTION_MATRIX u_projection
#endif
#endif

#ifndef SSAO_NOISE2_FNC
#define SSAO_NOISE2_FNC(ST) random2(ST)
#endif

#ifndef SSAO_NOISE3_FNC
#define SSAO_NOISE3_FNC(POS) random3(POS)
#endif

#ifndef SSAO_DEPTH_BIAS
#define SSAO_DEPTH_BIAS 0.05
#endif

#ifndef FNC_SSAO
#define FNC_SSAO

#if defined(CAMERA_NEAR_CLIP) && defined(CAMERA_FAR_CLIP)

float ssao(SAMPLER_TYPE texDepth, vec2 st, vec2 pixel, float radius) {

    #if defined(SSAO_NOISE_ARRAY)
    float noiseS    = sqrt(float(SSAO_NOISE_NUM));
    int  noiseX     = int( mod(gl_FragCoord.x - 0.5, noiseS) );
    int  noiseY     = int( mod(gl_FragCoord.y - 0.5, noiseS) );
    vec2 noise      = SSAO_NOISE_ARRAY[noiseX + noiseY * int(noiseS)].xy;
    #else
    vec2 noise = SSAO_NOISE2_FNC( st ); 
    #endif
    noise *= 0.1;

    float depth     = depth2viewZ( SAMPLER_FNC( texDepth, st ).r ) + SSAO_DEPTH_BIAS * 0.5; 
    float ao        = 0.0;

    // if (depth < 0.99) 
    {
        float w = pixel.x / depth + noise.x;
        float h = pixel.y / depth + noise.y;

        float dz = 1.0 / float( SSAO_SAMPLES_NUM ); 
        float l = 0.0; 
        float z = 1.0 - dz * 0.5; 

        for ( int i = 0; i < SSAO_SAMPLES_NUM; i ++ ) { 
            float r = sqrt( 1.0 - z ); 
            float pw = cos( l ) * r; 
            float ph = sin( l ) * r; 
            vec2 vv = radius * vec2( pw * w, ph * h);
            ao += ( step( depth2viewZ( SAMPLER_FNC( texDepth, st + vv).r ), depth) + 
                    step( depth2viewZ( SAMPLER_FNC( texDepth, st - vv).r ), depth) ) * 0.5;
            z = z - dz; 
            l = l + 2.399963229728653; 
        } 
        ao = 1.0 - ao * dz;
        ao = saturate( 1.98 * ( 1.0 - ao ) );
    }
    return ao;
}
#endif

#if defined(SSAO_SAMPLES_ARRAY)

float ssao(SAMPLER_TYPE texPosition, SAMPLER_TYPE texNormal, vec2 st, float radius) {
    vec4  position  = SAMPLER_FNC(texPosition, st);
    vec3  normal    = SAMPLER_FNC(texNormal, st).rgb;

    #if defined(SSAO_NOISE_ARRAY) 
    float noiseS    = sqrt(float(SSAO_NOISE_NUM));
    int   noiseX    = int( mod(gl_FragCoord.x - 0.5, noiseS) );
    int   noiseY    = int( mod(gl_FragCoord.y - 0.5, noiseS) );
    vec3  noise     = SSAO_NOISE_ARRAY[noiseX + noiseY * int(noiseS)];
    #else
    vec3  noise     = SSAO_NOISE3_FNC( position.xyz ); 
    #endif

    vec3 tangent    = normalize(noise - normal * dot(noise, normal));
    vec3 binormal   = cross(normal, tangent);
    mat3 tbn        = mat3(tangent, binormal, normal);

    float occlusion = 0.0;
    for (int i = 0; i < SSAO_SAMPLES_NUM; ++i) {
        vec3 samplePosition = tbn * SSAO_SAMPLES_ARRAY[i];
        samplePosition = position.xyz + samplePosition * radius;

        vec4 offsetUV = vec4(samplePosition, 1.0);
        offsetUV = CAMERA_PROJECTION_MATRIX * offsetUV;
        offsetUV.xy /= offsetUV.w;
        offsetUV.xy = offsetUV.xy * 0.5 + 0.5;

        float sampleDepth = SAMPLER_FNC(texPosition, offsetUV.xy).z;
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(position.z - sampleDepth));
        occlusion += (sampleDepth >= samplePosition.z + SSAO_DEPTH_BIAS ? 1.0 : 0.0) * rangeCheck;
    }

    occlusion /= float(SSAO_SAMPLES_NUM);
    return 1.0-occlusion;
}
#endif

#endif

Licenses

LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.

Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).

It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.

Get the latest news and releases

Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository