View Full Version : deferred stencil lights and the 3d math jazz

10-13-2014, 11:52 AM
hello all,

I'm having trouble understanding the math behing lightvolumes in a deferred shading process. I've implemented a solution that works but I don't exactly understand why it works the way it does. Here's essentially what I do:

setting up a g-buffer for albedo, depth, lightmaps and normals. Depth buffer is GL_DEPTH24_STENCIL8.
1. pass: render the geometry in the g-buffer.
2. pass for every light in the scene draw a sphere at (0,0,0) and scale/translate the sphere to fit the lights radius and position.
enable stencil depth to increment/decrement when the viewray enters/leaves the sphere.
3. pass render pointlights only in the area inside the sphere by querying stencil.
Blit the rest of the geometry from g-buffer to fragment shader color output.

Now this all works nice and I believe I got the implementation right. But when it comes to the math to calculate the lights illumination in the light shader I still fail to wrap my mind around it.

Here is what I did and don't understand:

Now the lights fragment shader looks like this. because the lightsource is always inside the sphere at its center and the sphere is being translated/scaled, the lights position itself is always (0,0,0). I've done all my math in eye/view space.

void main(void) {
io_texcoord = li_texcoord;
io_lightpos = u_mvmatrix * vec4(0,0,0,1); // get lights center position in eye space
gl_Position = u_pjmatrix * u_mvmatrix * li_vtx;

Here comes the fragment shader and this one gives me a headeache:

To get the fragments 3D position in view space I derive my position from depth buffer:

u_pjimatrix is the transposed inverse projectionmatrix.
Please note I have to TRANSPOSE the matrix in order to make the lighting work. I do not understand why I have to do this! :confused:
To transpose the projectionmatrix I set the transpose variable in glUniformMatrix4fv() to GL_TRUE.

vec3 GetPositionFromDepth(vec2 t) {
float depth = texture(u_depth, t).r; // get depth in clip space
vec4 p = vec4(t.xy * 2 - 1, depth * 2 - 1, 1) * u_pjimatrix;
p /= p.w;
return p.xyz;

The actual code to calculate the light is something I got to work by trial and error but I still not quite understand:

vec4 DoPointLight(vec3 pos, vec3 n) {
vec3 ldir = pos - io_lightpos.xyz; // direction should be in eye space, right?
// maybe someone can explain why this code works with in inverse transposed projection matrix for
// deriving the eye space pixel 3D position?
float radsqr = u_lrad * u_lrad;
float dist = min(dot(ldir, ldir), radsqr) / radsqr;
ldir = normalize(ldir);
float diffuseDot = dot(n, ldir);
vec3 diffuse = vec3(1) * clamp(diffuseDot, 1.0, 1.0) * (1.0 - dist);
return vec4(diffuse, 1);

the main of the fragment shader is very strightforward:

void main(void) {
vec2 t = vec2(gl_FragCoord.x / screenx, gl_FragCoord.y / screeny);
vec4 c = texture(u_albedo, t);
vec4 l = texture(u_light, t);
vec3 n = texture(u_normal, t).rgb;
vec3 p = GetPositionFromDepth(t); // pixel pos in eye space

lo_fragcolor = clamp(c * vec4(u_lcolor, 1) * (l + DoPointLight(p, n)), 0, 1);

Now the above code works but it shouldn't since I have to transpose the inverse projectionmatrix to obtain the 3D pixel position in eye space. Please keep in mind the light is at the ogigin inside a translated sphere. Maybe someone can shade some light on this issue?

Thanks a bunch in advance