saski

10-13-2014, 12:52 PM

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

Saski

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

Saski