Advertisement

Deferred Shading - light buffer in shadow mapping

Started by May 11, 2019 01:43 PM
7 comments, last by WhiskyAndCode 5 years, 8 months ago

Hello everyone.

I need help with my problem.
In my framebuffer (deffered shading) I have my light attachment, albeto, normal and also my shadow mapping.
In my "finalshader" I combine everything, but it seems that the shadow is overlapping and "erasing" the light:

alllllll.thumb.png.e8740961696345537347a551bf19e14f.png

this is my light attachment:

lightBuffer.thumb.png.fda9644c274051bd8c141242567b30b4.png

adssd.png.4b5ce11152d862ccfd393d7ebfffb731.png

and my shadow (is still raw, with no filters or smoothing):

shadow.thumb.png.fc18e761c30c061b87679b6dcc1329a4.png

What do I need to do so that light overlaps the shadow, and not the shadow overlapping the light?

My final shader to combine:


//summed up uniforms
uniform sampler2D DiffuseBuffer;
uniform sampler2D LightBuffer;
uniform sampler2D DepthBufferShadow;

uniform mat4 View;
uniform mat4 Proj;
uniform mat4 World;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform mat4 lightSpaceMatrix;
//the shadow calcs
float ShadowCalculation(vec4 fragPosLightSpace)
{
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;
    float closestDepth = texture(DepthBufferShadow, projCoords.xy).r; 
    float currentDepth = projCoords.z;
	vec3 normal = normalize(texture2D(NormalBuffer, TexCoord).xyz);
	vec3 FragPos = texture(PositionBuffer, TexCoord).xyz;
    vec3 lightDir = normalize(lightPos - FragPos);
    float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
    float shadow = 0.0;
    vec2 texelSize = 1.0 / textureSize(DepthBufferShadow, 0);
    for(int x = -1; x <= 1; ++x)
    {
        for(int y = -1; y <= 1; ++y)
        {
            float pcfDepth = texture(DepthBufferShadow, projCoords.xy + vec2(x, y) * texelSize).r; 
            shadow += currentDepth - bias > pcfDepth  ? 1.0 : 0.0;        
        }    
    }
    shadow /= 9.0;    
    if(projCoords.z > 1.0)
        shadow = 0.0;
        
    return shadow;
}
///......................

//in main 
vec3 FragPos = texture(PositionBuffer, TexCoord).xyz;
vec4 FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
float shadow = ShadowCalculation(FragPosLightSpace);
FragColor = ((texture2D(DiffuseBuffer, TexCoord)*(vec4(vec3((1.0 - shadow)), 1.0))))  * texture2D(LightBuffer, TexCoord);

 

Can someone help me ?

thank

You draw shadow after albedo and lights. Draw light it current pixel after shadow. It could be done in one pixel ubershader shader.

Advertisement

Here's a simple way to see it that would fix your problem.

A pixel may be affected by many source of light and each light may or may not have a shadow map.

You should iterate through all the lights that affect your pixel and accumulate their contribution which you seem to be doing already as I see two light properly mixing up together in your screenshot.

 

However you should also join your shadow calculation process to this. If you organized your data in a way that you can identify which light each shadow map are attached to, you should easily be able to skip the light calculation if the pixel is in the shadow of the current light but let the loop continue and other lights contribute to the final pixel color.

 

EDIT: Looking at your code I see that you may have added too many step to your deferred renderer. Light and shadow should be done in the same pass. Not only will you avoid doing an expensive light calculation if the pixel is shadowed but you'll also fix these kind of problem.

Hello all. thank for reply.

in fact, so far only the "sunlight" will cast shadow. These other lights were still only to give better effect on the particles (fires, shots and explosions). That is, my lighting shader will not cast shadow. Sorry for not talking to you before.
Do you have any idea ?

32 minutes ago, WhiskyAndCode said:

Hello all. thank for reply.

in fact, so far only the "sunlight" will cast shadow. These other lights were still only to give better effect on the particles (fires, shots and explosions). That is, my lighting shader will not cast shadow. Sorry for not talking to you before.
Do you have any idea ?

Isn't the "sunlight" already calculated just like any of your other lights in your lighting shader?

The problem you're having will remain if you "combine" the shadow and light only at the end as some lights still need to contribute to the pixel color even if it's in the shadow of another one. You're entering a whole world of patch and hacks if you want to get it working this way. The simplest way to fix it is to bring your ShadowCalculation function and the shadow map in your lighting shader and drop the combine process.

 

My engine also has a deferred pipeline and a light is a light, no matter if it's a directional light for the sun or an orange point light to make a fire look more real. Light and shadow are both calculated during the same pass which make that particular problem very easy to solve. In this screenshot the orange light doesn't cast shadow and it work even if we are in the shadow of another light.

image.thumb.png.2592c7fb1bab9b92b0bc3d180647ba2d.png

46 minutes ago, ChuckNovice said:

The simplest way to fix it is to bring your ShadowCalculation function and the shadow map in your lighting shader and drop the combine process.

I understand.

I can not think how to put the lights and shadow mapping on my lighting shader. For I render "spheres" in separate lighting passes, according to my shader:


out vec4 LightMap;       

uniform vec2 ScreenSize;

uniform float LightRadius;
uniform vec3 LightColor;
uniform float LightIntensity;
uniform vec3 LightCenter;

uniform sampler2D PositionBuffer;
uniform sampler2D NormalBuffer;

void main()
{
    vec2 texCoord = gl_FragCoord.xy / ScreenSize;

    vec3 pixelPos = texture2D(PositionBuffer,texCoord).xyz;
    vec3 pixelNormal = normalize(texture2D(NormalBuffer, texCoord).xyz);

    float alpha = length(pixelNormal);
    if(alpha < 0.1)
    {
        LightMap = vec4(0,0,0,0);
        return;
    }

    vec3 toLight = LightCenter - pixelPos;

    float attenuation = clamp(1.0 - length(toLight)/LightRadius,0.0,1.0); 

    toLight = normalize(toLight);

    float nDotL = max(dot(pixelNormal, toLight),0.0);

    vec3 diffuseLight = LightColor * nDotL;

    LightMap = LightIntensity * attenuation * vec4(diffuseLight, alpha);
}

I tried to change, and.... the result:

image.png.e5756f36f772342f8a05523e19c87a62.pngimage.png.146399ffdbe10677979d94ab5f1ac867.png

Now source of light:



out vec4 LightMap;       

uniform vec2 ScreenSize;
//.....
uniform sampler2D PositionBuffer;
uniform sampler2D NormalBuffer;
uniform sampler2D DepthBufferShadow;
uniform sampler2D TextCoordBuffer;

uniform mat4 View;
uniform mat4 Proj;
uniform mat4 World;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform mat4 lightSpaceMatrix;

float ShadowCalculation(vec4 fragPosLightSpace)
{
    vec2 TexCoord = gl_FragCoord.xy / ScreenSize;
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;
    float closestDepth = texture(DepthBufferShadow, projCoords.xy).r; 
    float currentDepth = projCoords.z;
	vec3 normal = normalize(texture2D(NormalBuffer, TexCoord).xyz);
	vec3 FragPos = texture(PositionBuffer, TexCoord).xyz;
    vec3 lightDir = normalize(lightPos - FragPos);
    float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
    float shadow = 0.0;
    vec2 texelSize = 1.0 / textureSize(DepthBufferShadow, 0);
    for(int x = -1; x <= 1; ++x){
        for(int y = -1; y <= 1; ++y){
            float pcfDepth = texture(DepthBufferShadow, projCoords.xy + vec2(x, y) * texelSize).r; 
            shadow += currentDepth - bias > pcfDepth  ? 1.0 : 0.0;        
        }    
    }
    shadow /= 9.0;
    
    if(projCoords.z > 1.0)
        shadow = 0.0;
        
    return shadow;
}

void main()
{
    vec2 texCoord = gl_FragCoord.xy / ScreenSize;

    vec3 pixelPos = texture2D(PositionBuffer,texCoord).xyz;
    vec3 pixelNormal = normalize(texture2D(NormalBuffer, texCoord).xyz);

    float alpha = length(pixelNormal);
    if(alpha < 0.1){
        LightMap = vec4(0,0,0,0);
        return;
    }

    vec3 toLight = LightCenter - pixelPos;
    float attenuation = clamp(1.0 - length(toLight)/LightRadius,0.0,1.0); 

    toLight = normalize(toLight);
    float nDotL = max(dot(pixelNormal, toLight),0.0);
    vec3 diffuseLight = LightColor * nDotL;  

	vec3 FragPos = texture(PositionBuffer, texCoord).xyz;
	vec4 FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
	float shadow = ShadowCalculation(FragPosLightSpace);

	LightMap = LightIntensity * attenuation * (vec4(diffuseLight, alpha)*vec4(vec3(1.0 - shadow), 1.0));
}

my question would be how to put it together and compose my shadow and lights?

Can u help me to change?

Advertisement

Mmmm there might be more to change than I thought. You are doing only one light per pass, reading your whole GBuffer everytime and you got a different shader for each type of light?

The sunlight and the red/green point lights should all be done in the same shader. For that to work you should have an array of all your lights passed to your shader so you can easily loop in all of them in one single pass.

On my side all the lights are stored in one big array as a linked list no matter if it's a point light, directional light or spot light. Each light points to the next one of the same type within the collection. Here the relevant parts of my shader. It's in HLSL but still give the big picture :

 


//---------------------------------------------------------------------------------------
// Defines the data structure of a light projection.
//---------------------------------------------------------------------------------------
struct LightProjection 
{
	// 64 bytes pack
	float4x4 ViewProjection;

	// 16 bytes pack
	float Near;
	float Far;
	float2 ShadowMapTileSize;
	
	// 16 bytes pack
	float3 ShadowMapTileOffset;
	bool HasShadowMap;
};

//---------------------------------------------------------------------------------------
// Defines the data structure of a light.
//---------------------------------------------------------------------------------------
struct Light
{
	// 16 bytes pack
	float3 Position;
	float FalloffStart;

	// 16 bytes pack
	float3 Direction;
	float FalloffEnd;

	// 16 bytes pack
	float3 Color;
	float SpotAngle;

	// 96 aligned pack
	LightProjection Projection;

	// 16 bytes pack
	bool Enabled;
	float ShadowBias;
	int ShadowQuality;
	int Next;
};

StructuredBuffer<Light> _dynamicLights : register(t0); // collection of dynamic lights.


//--------------------------------------------------
//	The part of the shader that handle the light/shadow
//--------------------------------------------------

	int dynamicDirectionalLightIndex = FrameConstantBuffer.DynamicDirectionalLightStartIndex;
	int dynamicPointLightIndex = FrameConstantBuffer.DynamicPointLightStartIndex;
	Light light;
	float3 accumulatedLight;
	// loop through the directional lights.
	while (dynamicDirectionalLightIndex != -1)
	{
		light = _dynamicLights[dynamicDirectionalLightIndex];
		accumulatedLight += ComputeDirectionalLight(light, attributes);
		dynamicDirectionalLightIndex = light.Next;
	}
	// loop through the point lights.
	while (dynamicPointLightIndex != -1)
	{
		light = _dynamicLights[dynamicPointLightIndex];
		accumulatedLight += ComputePointLight(light, attributes);
		dynamicPointLightIndex = light.Next;
	}

 

In this example, ComputeDirectionalLight and ComputePointLight both check the shadow map of the current light and immediately return 0, 0, 0 if it's shadowed before trying to calculate the light contribution which make it that easy to get the result that you saw in the screenshot of my previous post.

in fact, the sunlight is in the shadow mapping calculation, which is now in the pointlight shader.
My pointlight shader is passed every time for each light.
Then in the end I had my combination shader and there I did the shadow mapping calculation.

But I think I'm going to give it up for now and continue to study learnopengl.
Even so, thank you for the help and shared knowledge ChuckNovice.
I hope you can help me with the next questions ?.

Thank you

This topic is closed to new replies.

Advertisement