PDA

View Full Version : Getting harsh shadows with Deferred Lighting (video showing problem)



Jamez0r
12-31-2017, 02:22 PM
Hey guys, here is an video of the problem: https://youtu.be/4nXbgaZw3x8

I'm designing a lighting system in my game, and originally was using the shader from this tutorial: https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6

I was able to implement that, and it looked great (its the first one you see in the video of the problem).

Now I'm adapting that version to a "deferred lighting" system so that I can use a lot of lights, and although it seems to be working, the shadows near the center of the light look harsh/bad. It also looks like the original shader can actually 'brighten' the scene, whereas the new-shader can only darken (add shadows) since the blending is set to multiply (see the "rundown" section below). EDIT: After taking another look at the video, it seems like this might be the main issue (not being able to 'brighten' the scene). Not sure what to do though.

This is the shader from "ShaderLesson6" that I was previously using, and looked good:


//GL ES specific stuff
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif

//attributes from vertex shader
varying LOWP vec4 vColor;
varying vec2 vTexCoord;

//our texture samplers
uniform sampler2D u_texture; //diffuse map
uniform sampler2D u_normals; //normal map

//values used for shading algorithm...
uniform vec2 Resolution; //resolution of screen
uniform vec3 LightPos; //light position, normalized
uniform LOWP vec4 LightColor; //light RGBA -- alpha is intensity
uniform LOWP vec4 AmbientColor; //ambient RGBA -- alpha is intensity
uniform vec3 Falloff; //attenuation coefficients

void main() {
//RGBA of our diffuse color
vec4 DiffuseColor = texture2D(u_texture, vTexCoord);

//RGB of our normal map
vec3 NormalMap = texture2D(u_normals, vTexCoord).rgb;

//The delta position of light
vec3 LightDir = vec3(LightPos.xy - (gl_FragCoord.xy / Resolution.xy), LightPos.z);

//Correct for aspect ratio
LightDir.x *= Resolution.x / Resolution.y;

//Determine distance (used for attenuation) BEFORE we normalize our LightDir
float D = length(LightDir);

//normalize our vectors
vec3 N = normalize(NormalMap * 2.0 - 1.0);
vec3 L = normalize(LightDir);

//Pre-multiply light color with intensity
//Then perform \"N dot L\" to determine our diffuse term
vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(N, L), 0.0);

//pre-multiply ambient color with intensity
vec3 Ambient = AmbientColor.rgb * AmbientColor.a;

//calculate attenuation
float Attenuation = 1.0 / ( Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) );

//the calculation which brings it all together
vec3 Intensity = Ambient + Diffuse * Attenuation;
vec3 FinalColor = DiffuseColor.rgb * Intensity;
gl_FragColor = vColor * vec4(FinalColor, DiffuseColor.a);
}";


This is the "deferred lighting" shader that I'm using now, which is basically the same thing except this new one is used for *each* light, and only calculates the light's intensity:


#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif

//attributes from vertex shader
varying LOWP vec4 vColor;
varying vec2 vTexCoord;

//texture samplers
uniform sampler2D u_texture;

//values for shading algorithm
uniform vec2 resolution; //Resolution of Screen
uniform vec3 lightPosition; //Position of Light, normalized
uniform LOWP vec4 lightColor; //Light RGBA -- alpha is intensity
uniform vec3 falloff; //attenuation coefficients
uniform float zoomModifier; //Inverse of camera zoom (1f/camera.zoom)

void main() {

//RGBA of our normal map
vec4 normalMapColor = texture2D(u_texture, vTexCoord);

//The Delta position of light
vec3 lightDir = vec3(lightPosition.xy - (gl_FragCoord.xy / resolution.xy), lightPosition.z);

//Correct for aspect ratio
lightDir.x *= resolution.x / resolution.y;

//Correct for camera zoom
lightDir.xy *= zoomModifier;

//Determine distance (used for attenuation) BEFORE we normalize our LightDir
float D = length(lightDir);

//Normalized-normalMapColor
vec3 N = normalize(normalMapColor * 2.0 - 1.0);

//Normalized-lightDirection
vec3 L = normalize(lightDir);

//Pre-multiply light color with intensity, then perform "N dot L" to determine diffuse term
vec3 diffuse = (lightColor.rgb * lightColor.a) * max(dot(N,L), 0.0);

//Calculate attenuation
float attenuation = 1.0 / (falloff.x + (falloff.y * D) + (falloff.z * D * D));

//Calculate intensity
vec3 finalIntensity = diffuse * attenuation;

gl_FragColor = vColor * vec4(finalIntensity.rgb, normalMapColor.a);
}


This is the rundown of what I'm doing with the new shader:

1) Render the scene normally to the screen (default shader, regular textures)

2) Draw all of my normal-maps to a FrameBuffer ("normalMapBuffer")

3) On another FrameBuffer ("intensityFrameBuffer"), clear with the ambient light color

4) Set blending to additive (GL_ONE, GL_ONE)

5) For each of my lights, draw light to the intensityFrameBuffer using the normalMapBuffer texture and the newly-written shader

6) Set blending to multiply (GL_DST_COLOR, GL_ZERO)

7) Draw the intensityFrameBuffer to the screen



I'm pretty new to lighting/shaders, so I'm *hoping* its something simple I'm doing wrong, like using the wrong blending settings.

Original Shader with light intensity = 1:

https://i.imgur.com/rTjZn5f.png

Original Shader with light intensity = 2:

https://i.imgur.com/ZcRjgwF.png

New Shader with light intensity = 1:

https://i.imgur.com/YWMtese.png

New Shader with light intensity = 2:

https://i.imgur.com/4SeV3nF.png

Dark Photon
12-31-2017, 04:36 PM
...whereas the new-shader can only darken (add shadows) since the blending is set to multiply
...
6) Set blending to multiply (GL_DST_COLOR, GL_ZERO)
...


You can do whatever you want, but if you want your lighting contributions to add to each other, you need to use an additive blending function.

Moreover, if you want them to add correctly in a physically-realistic way, you need to use gamma-correct rendering and realistic material properties.

NOTE: Shadow is the absence of a lighting contribution, not some artificial area where we've "sucked up" the existing illumination and thrown it away.

Jamez0r
12-31-2017, 06:45 PM
You can do whatever you want, but if you want your lighting contributions to add to each other, you need to use an additive blending function.

Moreover, if you want them to add correctly in a physically-realistic way, you need to use gamma-correct rendering and realistic material properties.

NOTE: Shadow is the absence of a lighting contribution, not some artificial area where we've "sucked up" the existing illumination and thrown it away.

Thanks for the help Photon and may the Schwartz be with you!

My first goal is to recreate the lighting that I had in the original shader, but I saved a note to research gamma-correction / material properties so thanks for those keywords :)

Keep in mind I'm pretty new to this so I'm fumbling my way through. I think I was incorrect in my complaint of how "harsh/pixelated" the shadows were. I think the only difference between the original shader's outcome and my new one is that I am ONLY rendering the shadows (though the multiply-blending) on the new one. I understand now that in order to have "highlights" I need an additive blending function.

So I tried a couple things and came up with this:

1) Render the scene normally to the screen (default shader, regular textures)

2) Draw all of my normal-maps to a FrameBuffer ("normalMapBuffer")

3) On another FrameBuffer ("intensityFrameBuffer"), clear with the ambient light color

4) Set blending to additive (GL_ONE, GL_ONE)

5) For each of my lights, draw light to the intensityFrameBuffer using the normalMapBuffer texture and the newly-written shader

6) Set blending to multiply (GL_DST_COLOR, GL_ZERO)

7) Draw the intensityFrameBuffer to the screen

8) [NEW] Set blending back to additive (GL_ONE, GL_ONE)

9) [NEW] On another FrameBuffer ("highlightFrameBuffer"), clear with Color(0,0,0,1)

10) [NEW] Render each of the light's intensity onto the highlightFrameBuffer (same as step 5, but this time the FrameBuffer wasn't cleared with the ambient color)

11) [NEW] Set blending mode to (GL_DST_COLOR, GL_ONE) --> I tried (GL_ONE, GL_ONE), but it didn't look as good. I'm totally guess-and-check'ing here, so if there is a recommended mode for this I'd be glad to learn about it

12) [NEW] Render the highlightFrameBuffer to the screen


The results looked pretty close to the original!

https://i.imgur.com/CaEddGT.png


So my question now... is there a more efficient way to have done this, or any other tips/suggestions?

Thanks!!