Search code examples
openglglsllighting

Volumetric Light not rendering volume correctly


I've been following this tutorial (https://www.programmersought.com/article/68075912719/) to get volumetric light. But I am not getting the correct output.

enter image description here

enter image description here

The shadow volume is incorrectly rendered and I am not sure what I am doing wrong. My vertex and fragment shader looks exactly like the tutorial but still I'm not getting correct output.

Here is the vertex shader code

#version 450 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;

uniform mat4 model;
uniform mat4 vp;

uniform float zFar;
uniform float fov;;
uniform float aspectRatio;

out vec2 TexCoord;
out vec2 farPlanePos;

void main(void){

gl_Position =  vec4(position, 1.0);

TexCoord = vec2 (texCoord.x, 1 - texCoord.y);

float t = tan(fov/2);
farPlanePos.x = (TexCoord.x * 2 - 1) * zFar * t * aspectRatio;
farPlanePos.y = (TexCoord.y * 2 - 1) * zFar * t;

}

And Here is the fragment shader code

#version 450 core

in vec2 TexCoord;


uniform vec3 cameraPos;
uniform vec3 lightPos;
uniform vec3 lightColor;

uniform mat4 invViewMatrix;
uniform mat4 invProjectionMatrix;

uniform float ambientStrength;

uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform sampler2D gDepth;

uniform sampler2D shadowMapTexture;


in vec2 farPlanePos;

uniform float zFar;
uniform float zNear;


float g = 0.0f;




uniform mat4 lightViewMatrix;
uniform mat4 projectionMatrix;

uniform mat4 viewMatrix;


vec3 yellow_light = vec3(1,198.0/255.0,107.0/255.0);

out vec4 finalColor;

// use linear z depth
vec3 ComputeWorldPos(float depth){

vec4 pos = vec4(vec3(farPlanePos.x, farPlanePos.y, -zFar) * depth , 1.0f);
vec4 ret = invViewMatrix * pos;
return ret.xyz / ret.w;    
}


bool IsInShadow(vec4 worldPos){

float fShadow = 0.0;
vec4 lightPos = (lightViewMatrix * (worldPos));
float fDistance = -lightPos.z / zFar;

lightPos = projectionMatrix * lightPos;

vec2 uv = lightPos.xy / lightPos.w * 0.5 + vec2(0.5f, 0.5f);

uv.x = clamp(uv.x, 0.0f, 1.0f);
uv.y = clamp(uv.y, 0.0f, 1.0f);

float offset = 0.5f/zFar;

float distanceMap = texture2D(shadowMapTexture, uv).r;
return fDistance - offset > distanceMap;
}

void main(void){

 float depth = texture2D(gDepth, TexCoord).w;

 vec3 total_light;

// volume light
{
    float I = 0.0f;
    float d = depth * zFar;
    int virtual_plane_num = 100;
    int begin = int(virtual_plane_num * zNear / (d - zNear));
    int end = int(virtual_plane_num * (zFar - d) / (d - zNear));
    for(int j = begin; j <= virtual_plane_num + begin; j++)
    {
        float z = 1.0f * j / (begin + virtual_plane_num + end);

        vec3 pos = ComputeWorldPos(z);

        if(z < depth && !IsInShadow(vec4(pos,1.0f)))
        {
            //vec3 lightDis = pos - lightPos;
            //vec3 viewDis = pos - cameraPos;

            //float lightDis2 = lightDis.x * lightDis.x + lightDis.y * lightDis.y  + lightDis.z * lightDis.z;

            vec3 lightDir = normalize(pos - lightPos);
            vec3 viewDir = normalize(pos - cameraPos);

            float cosTheta = dot(lightDir,normalize(-lightPos));

            float hg = 1.0f/(4.0f*3.14f)* (1.0f - g*g)/ pow(1.0f + g * g - 2.0f * g * dot(lightDir,-viewDir), 1.5f);
            
            if(cosTheta >0.9){
                I += clamp(10 * hg / virtual_plane_num, 0.0f, 1.0f);
            }
        }        
    }

    I = clamp(I , 0.0f,1.0f);
    total_light += I * yellow_light;

}


    vec3 normal =   texture2D(gNormal, TexCoord).xyz * 2 - 1; //result.xyz * 2 - 1;
    vec3 worldPos = ComputeWorldPos(depth);


// parallel lights
/*
{
    vec3 ViewDir = normalize( cameraPos - worldPos );
    vec3 lightDir = normalize(vec3(0.5,1,0.2) );
    vec3 halfDir = normalize(lightDir + ViewDir);
    float diffuse = 0.3 * clamp(dot(normal, lightDir), 0, 1) ;
    vec3 reflectDir = normalize(reflect(-lightDir,normal));
    float specular =  0.3 * pow(clamp(dot(reflectDir,halfDir),0,1),50.0);
    vec3 color = (diffuse + specular) *vec3(1,1,1);
    total_light += color;
}
*/

vec3 color = vec3(texture2D(gAlbedoSpec,TexCoord));
float ambient = 0.1;

finalColor = vec4(total_light + ambient * color,1);
}

So you can see the vertex and fragment shader code is exactly like the blog, but still the output is different.

Unfortunately it doesn't say how to contact the blogger otherwise I would have asked them directly. So the next best option is stockoverflow, so I am asking here.


Solution

  • Ok after 2 days I was able to fix the issue. I think the isInShadow function is incorrect as it always perspective divies by zFar and also doesnt multiply by projection matrix before getting current depth.

    So I replaced the code with learnOpengl shadow calculation as below.

    bool IsInShadow(vec4 worldPos){
    
    
    vec4 lightPos = (lightViewMatrix * (worldPos));
    
    //float fDistance = -lightPos.z/ zFar;
    
    lightPos = projectionMatrix * lightPos;
    
    vec3 projCoords = lightPos.xyz / lightPos.w ;
    
    projCoords = projCoords* 0.5 + 0.5f;
    
    //uv.x = clamp(uv.x, 0.0f, 1.0f);
    //uv.y = clamp(uv.y, 0.0f, 1.0f);
    
    float offset = 0.5f/zFar;
    
    float distanceMap = texture2D(shadowMapTexture, projCoords.xy).r;
    return projCoords.z - offset > distanceMap;
    
    }
    

    And now the code works!!

    enter image description here