LYGIA Shader Library

volumetricLightScattering (lygia/lighting/volumetricLightScattering)

ScreenSpace Reflections

Dependencies:

Use:

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

Check it on Github



#ifndef VOLUMETRICLIGHTSCATTERING_FACTOR
#define VOLUMETRICLIGHTSCATTERING_FACTOR 0.25
#endif

#ifndef VOLUMETRICLIGHTSCATTERING_STEPS
#define VOLUMETRICLIGHTSCATTERING_STEPS 50
#endif

#ifndef VOLUMETRICLIGHTSCATTERING_TYPE
#define VOLUMETRICLIGHTSCATTERING_TYPE float
#endif

#ifndef CAMERA_POSITION
#define CAMERA_POSITION     u_camera
#endif

#ifndef CAMERA_NEAR_CLIP
#define CAMERA_NEAR_CLIP    u_cameraNearClip
#endif

#ifndef CAMERA_FAR_CLIP
#define CAMERA_FAR_CLIP     u_cameraFarClip
#endif

#ifndef CAMERA_PROJECTION_MATRIX
#define CAMERA_PROJECTION_MATRIX u_projectionMatrix
#endif

#ifndef INVERSE_CAMERA_PROJECTION_MATRIX
// #define INVERSE_CAMERA_PROJECTION_MATRIX u_inverseProjectionMatrix
#define INVERSE_CAMERA_PROJECTION_MATRIX inverse(CAMERA_PROJECTION_MATRIX)
#endif

#ifndef CAMERA_VIEW_MATRIX
#define CAMERA_VIEW_MATRIX u_viewMatrix
#endif

#ifndef INVERSE_CAMERA_VIEW_MATRIX
// #define INVERSE_CAMERA_VIEW_MATRIX u_inverseViewMatrix
#define INVERSE_CAMERA_VIEW_MATRIX inverse(CAMERA_VIEW_MATRIX)
#endif

#ifndef LIGHT_MATRIX
#define LIGHT_MATRIX        u_lightMatrix
#endif

#ifndef LIGHT_POSITION
#define LIGHT_POSITION     (LIGHT_MATRIX * vec4(0.0,0.0,-1.0,1.0)).xyz
#endif

// https://www.alexandre-pestana.com/volumetric-lights/
// https://www.gamedev.net/blogs/entry/2266308-effect-volumetric-light-scattering/

#ifndef VOLUMETRICLIGHTSCATTERING_SAMPLE_FNC
#define VOLUMETRICLIGHTSCATTERING_SAMPLE_FNC(TEX, UV) min(SAMPLER_FNC(TEX, UV).r, 0.997)
#endif

#ifndef FNC_VOLUMETRICLIGHTSCATTERING
#define FNC_VOLUMETRICLIGHTSCATTERING
VOLUMETRICLIGHTSCATTERING_TYPE volumetricLightScattering(SAMPLER_TYPE lightShadowMap, const in mat4 lightMatrix, const in vec3 lightPos, const in vec3 rayOrigin, const in vec3 rayEnd) {
    vec3  rayVector     = rayEnd - rayOrigin;
    float rayLength     = length(rayVector);
    vec3  rayDirection  = rayVector / rayLength;
    float stepLength    = 1.0 / float(VOLUMETRICLIGHTSCATTERING_STEPS);
    float rayStepLength = rayLength * stepLength;
    vec3  rayStep       = rayDirection * rayStepLength;
    float lightDotView  = dot(rayDirection, normalize(lightPos));

    float scattering_g  = VOLUMETRICLIGHTSCATTERING_FACTOR * VOLUMETRICLIGHTSCATTERING_FACTOR;
    float scattering    = 1.0 - scattering_g;
    scattering /= (4.0 * PI * pow(1.0 + scattering_g - (2.0 * VOLUMETRICLIGHTSCATTERING_FACTOR) * lightDotView, 1.5));

    VOLUMETRICLIGHTSCATTERING_TYPE L = VOLUMETRICLIGHTSCATTERING_TYPE(0.0);
    vec4  rayCurrPos    = vec4(rayOrigin, 1.0);

    for (int i = 0; i < VOLUMETRICLIGHTSCATTERING_STEPS; i ++) {
        vec4 worldInShadowCameraSpace = lightMatrix * rayCurrPos;
        worldInShadowCameraSpace /= worldInShadowCameraSpace.w;
        float shadowMapValue = VOLUMETRICLIGHTSCATTERING_SAMPLE_FNC(lightShadowMap, worldInShadowCameraSpace.xy );
        L +=    step(worldInShadowCameraSpace.z, shadowMapValue) * 
                #ifdef VOLUMETRICLIGHTSCATTERING_MASK_FNC
                VOLUMETRICLIGHTSCATTERING_MASK_FNC(worldInShadowCameraSpace) *
                #endif
                scattering;

        rayCurrPos.xyz += rayStep; 
    }

    return L * stepLength;
}

VOLUMETRICLIGHTSCATTERING_TYPE volumetricLightScattering(SAMPLER_TYPE texDepth, vec2 st) {
    float depth = VOLUMETRICLIGHTSCATTERING_SAMPLE_FNC(texDepth, st);
    float viewZ = depth2viewZ(depth, CAMERA_NEAR_CLIP, CAMERA_FAR_CLIP);

    #ifdef VOLUMETRICLIGHTSCATTERING_NOISE_FNC
    viewZ += VOLUMETRICLIGHTSCATTERING_NOISE_FNC;
    #endif

    vec4 worldPos = INVERSE_CAMERA_VIEW_MATRIX * screen2viewPosition(st, depth, viewZ);

    #ifdef LIGHT_SHADOWMAP
    return volumetricLightScattering(LIGHT_SHADOWMAP, LIGHT_MATRIX, LIGHT_POSITION, CAMERA_POSITION, worldPos.xyz);
    #else 
    return VOLUMETRICLIGHTSCATTERING_TYPE(0.0);
    #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