Deferred Shadow Mapping And Shading

Started by
8 comments, last by irreversible 13 years, 2 months ago
First off, I'm not following a single tutorial/resource, which is probably why I'm having some trouble getting the basics down correctly. First off, I think I have my MRT code down proper. So is the shader app-side code. Nevertheless, based on <a href="http://www.codinglabs.net/tutorial_def_rendering_1.aspx">this example</a>, I'm having some difficulty understanding the shader bit:

The vertex shader, as posted in that tutorial, looks like this (I've removed the non-essential bits):


varying vec4 normals;
varying vec4 position;
attribute vec3 vNormal, vTangent, vBiNormal;
uniform mat4 ModelMatrix;
uniform mat4 RotationMatrixInverse;

void main( void )
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
normals = vec4(gl_Normal,1); //ignoring the rotation matrix for now
position = ModelMatrix * gl_Vertex;

gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0);
}

From this I can't understand calculating the position - what is the ModelMatrix uniform supposed to be? Modelview or, more likely, modelview projection matrix? Why is he setting it as a uniform and not using the one provided by GLSL? Here's the MRT output for the three targets (diffuse, position and normal). I know the diffuse and normal targets are okay, but I'm not sure about the second one (I'm calculating the position as: position = gl_ModelViewMatrix * gl_Vertex). It certainly looks wrong as the color distribution within the viewport never changes.

deferredvp.png

Here's my pixel shader, as taken from the tutorial:


void main( void )
{
gl_FragData[0] = texture2D(tDiffuse, gl_TexCoord[0].st);
gl_FragData[0].a = 1;
gl_FragData[1] = vec4(position.xyz, 1);
gl_FragData[2] = vec4(normals.xyz, 1);
}


Well, that's for step one, anyway - since my post processing code is producing really wrong output, I thought I'd start checking everything from the top. If anyone can notice something wrong with this, I'd appreciate some suggestions. Otherwise the post processing code is probably to be blamed.
Advertisement
In typical graphics you've got three matrices:

Model Matrix (transforms object space to world space)
View Matrix (transforms world space to eye space)
Projection Matrix (transforms eye space to screen space)

The model matrix is just the first of those three. Old fixed pipeline combined the model and view matrix together into the modelview matrix because it never required them separately. If your deferred renderer is defining the "position" in world space than you need the model matrix alone to do this transform. If you multiply a vertex by the modelview matrix you're going to get it's position in eye space, which probably isn't what you want.

All the built in gl_blahMatrix matrices are deprecated now anyway, you're supposed to be managing all matrices yourself on the client side, and then use uniform matrices to upload them to the shader.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
Ah - righty.

I'm guessing then that with no transformations applied, the model matrix is identity, in which case - for fixed geometry - I can remove the multiplication altogether; after which it the three textures look like in att'd. Having, well, no prior experience with this level of shader development (the most I've done is set up a forward lighting shader) - or matrices for that matter, I'm rather confused about this. By managing the matrices myself you mean maintaining app-side model & view matrix stacks?

By managing the matrices myself you mean maintaining app-side model & view matrix stacks?


Yep. Nobody says you need a stack though, I typically just store one mat4 per model for the model matrix, and then pass down common view and projection matrices into the render loop.

If you're not in the mood to implement matrix math yourself, GLM is a pretty good library to use with OpenGL.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
I'm resuscitating this this thread as I seem to be getting a lot closer, but still not close enough for the cigar. Since the last post, I've switched to a viewspace approach and am trying to adapt this paper by Fabio Policarpo and Francisco Fonseca. I'm fairly certain that my MRT's are correct, but the post-processing is generating some unexpected results. The problem seems to be that there are some strange directional artifacts, probably in the way I'm handling the post-processing filter (lighting does change when I move the light around, but it also seems to change (along with the black streak artifact when I move the camera around). All in all, combine my rather daft mathematical skills and a certain case of braindeadness and the result is a fine case of confusion.

So, the attached image shows the my buffers as they are right now (diffuse, depth encoded as color and view-space normal) in the MRT windows and the combined result in the main window. Notice the strange lighting artifact. I've no idea what it's from - there's only one light source in the scene and it's on a vertical line at (0, 2.5, 0) while (0, 0, 0) is at the same level as the ground quad at the back (from the middle top corner in the image) edge of the cube.

As the MRT's seem to be right about what one would expect, I'll skip the GBuffer code and only post relevant code for the full screen filter (it computes and includes normals, but those are ignored for clarity in the actual lighting code - eg all the code should do is simple per-fragment attenuation-based lighting). I've also tested the depth encoding and it seems to be just fine.

VERTEX SHADER:

varying vec3 vViewPosition; //this is the unprojected position of the corner of the fullscreen quad passed in as TU1 texture coordinates (see below for the code)

void main( void )
{
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
vViewPosition = gl_MultiTexCoord1;

gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0);
}


FRAGMENT SHADER:

uniform sampler2D tImage0;
uniform sampler2D tImage1;
uniform sampler2D tImage2;
uniform vec3 vEyePosition;
uniform vec3 vEyeDirection;
uniform vec3 lightpos;
uniform vec2 planes;

varying vec3 vViewPosition;

vec3 lighting(vec3 Scolor, vec3 Spos, float Sradius, vec3 p, vec3 n, vec3 Mdiff, vec3 Mspec, float Mshi)
{
vec3 vLightDir = Spos - p;
// vec3 vViewVec = normalize(p);
// vec3 vHalfVec = normalize(vViewVec + vLightDir);


// if(dot(n, normalize(vLightDir)) < 0.0)
// return vec3(1, 0, 0);

// attenuation (equation 2)
float att = clamp(1.0 - length(vLightDir) / Sradius, 0.0, 1.0);
// vLightDir = normalize(vLightDir);


// diffuse and specular terms (equations 3 and 4)
// vec3 Idiff = clamp(dot(vLightDir, n), 0.0, 1.0) * Mdiff * Scolor;
// vec3 Ispec = pow(clamp(dot(vHalfVec, n), 0.0, 1.0), Mshi) * Mspec * Scolor;

// final color (part of equation 1)
//vec3 d = float_to_color((planes.x * p.z + planes.y) / -p.z);

//<<<<SKIP LIGHT COLOR, SPECULAR COMP., NORMALS AND STUFF FOR NOW - USE ONLY INTENSITY>>>
return att * Mdiff;//(Idiff/* + Ispec*/);
}

float color_to_float(vec3 color)
{
const vec3 byte_to_float=vec3(1.0,1.0/256,1.0/(256*256));
return dot(color,byte_to_float);
}

void main( void )
{
vec3 lightcolor = vec3(1, 1, 1);

float depth = color_to_float(texture2D(tImage1, gl_TexCoord[0].xy));

// view dir
vec3 view = normalize(vViewPosition);
// position
vec3 pos;
pos.z = -planes.y / (planes.x + depth);
pos.xy = view.xy / view.z * pos.z;

// normal
vec3 normal = texture2D(tImage2, gl_TexCoord[0].xy) - 0.5;//f3tex2D(normal_map,IN.texcoord)-0.5;
float len = length(normal);
if(len > 0.1)
normal /= len;
else
normal = 0;

// material information
vec3 diffuse = texture2D(tImage0, gl_TexCoord[0].xy);//f3tex2D(diffuse_map,IN.texcoord);
vec4 specular = vec4(0, 0, 0, 1);//f4tex2D(specular_map,IN.texcoord);

// lighting equation (see listing 2)
float fLightRadius = 20.0;
vec3 final_color = lighting(lightcolor, lightpos.xyz, fLightRadius, pos, normal, diffuse, specular.xyz, 0/*specular.w*/);

// return the final color
gl_FragColor = vec4(final_color.xyz, 1.0);
}


There's a bunch of extraneous code in the fragment shader, although it shouldn't be too much.

And finally, the relevant bits from the application side:




//FROM THE FILTER CODE
...
//set the light's position; lightpos is in world space
TVector3D l = lightpos;
IMatrix4f modelview;
glGetFloatv(GL_MODELVIEW_MATRIX, modelview);
l = modelview * l;

shader->SetUniform3f(uniLightPosition, (float)l.x, (float)l.y, (float)l.z);

DrawFullscreenQuad(drv);
...

//AND THE RELEVANT DrawFullscreenQuad() FUNCTION
DrawFullscreenQuad(IVideoDriver * drv)
{
int sx = drv->window->GetWidth();
int sy = drv->window->GetHeight();
int pixels[4][2] = { { 0, 0 }, { 0, sy }, { sx, sy }, { sx, 0} };
int viewport[4] = { 0, 0, sx, sy };

IMatrix4f view_rotation;

//in D3D:
// 1 2 3 4
//1 right.x up.x look.x 0
//2 right.y up.y look.y 0
//3 right.z up.z look.z 0
//4 pos.x pos.y pos.z 1

//in GL:
// 1 2 3 4
//1 right.x right.y right.z pos.x
//2 up.x up.y up.z pos.y
//3 look.x look.y look.z pos.z
//4 0 0 0 1

TVector3D r = drv->camera->Right;
TVector3D l = drv->camera->Orientation;
TVector3D u = drv->camera->UpVector;
TVector3D p = drv->camera->Position;

view_rotation[0 ] = l.x;
view_rotation[1 ] = l.y;
view_rotation[2 ] = l.z;
view_rotation[3 ] = 0;

view_rotation[4 ] = u.x;
view_rotation[5 ] = u.y;
view_rotation[6 ] = u.z;
view_rotation[7 ] = 0;

view_rotation[8 ] = r.x;
view_rotation[9 ] = r.y;
view_rotation[10] = r.z;
view_rotation[11] = 0;

view_rotation[12] = 0;
view_rotation[13] = 0;
view_rotation[14] = 0;
view_rotation[15] = 1;

//setting the translation row to identity
// view_rotation[12] = p.x;
// view_rotation[13] = p.y;
// view_rotation[14] = p.z;
// view_rotation[15] = 1;

double model_matrix[16];
double proj_matrix[16];

glGetDoublev(GL_MODELVIEW_MATRIX, model_matrix);
glGetDoublev(GL_PROJECTION_MATRIX, proj_matrix);

TVector3D v[4];
TVector3D distance;
for(int i = 0; i < 4; i++)
{
gluUnProject(
pixels[0],pixels[1],1,
model_matrix, proj_matrix, viewport,
&distance[0], &distance[1], &distance[2]);
v = distance;
v -= p;
v = VecNormalize(v);
v = view_rotation * v;
}

TVector3D v0 = TVector3D(0, 0, 0);
TVector3D v1 = TVector3D(sx, sy, 0);

drv->Begin(GD_QUADS);
//all calls here are essentially GL equivalents; the first argument of TexCoord3D() is the texture unit
drv->TexCoord3D(0, 0, 1, 0);
//TU1 texcoords are forwarded to the vViewPosition variable in the shader
drv->TexCoord3D(1, v[1]);
drv->Vertex3D(v0);

drv->TexCoord3D(0, 0, 0, 0);
drv->TexCoord3D(1, v[0]);
drv->Vertex3D(v0.x, v1.y, v1.z);

drv->TexCoord3D(0, 1, 0, 0);
drv->TexCoord3D(1, v[3]);
drv->Vertex3D(v1);

drv->TexCoord3D(0, 1, 1, 0);
drv->TexCoord3D(1, v[2]);
drv->Vertex3D(v1.x, v0.y, v0.z);
drv->End();
}


I think that's about it. I'm really not too sure about the code inside DrawFullscreenQuad(). The relevant snippet in the paper looks like this:


00 int pixels[4][2]={ { 0,0 },{0,sy},{sx,sy},{sx,0} };
01 int viewport[4]={ 0,0,sx,sy };
02
03 pMatrix view_rotation = view_matrix;
04 view_rotation.set_translate(0);
05
06 pVector v[4];
07 double d[3];
08 for( int i=0;i<4;i++ )
09 {
10 gluUnProject(
11 pixels[0],pixels[1],10,
12 model_matrix, proj_matrix, viewport,
13 &d[0],&d[1],&d[2]);
14 v.vec((float)d[0],(float)d[1],(float)d[2]);
15 v -= camera.pos;
16 v.normalize();
17 v = v*view_rotation;
18 }


Since I'm not maintaining my own matrices, I'm constructing one at runtime (I'll probably port my code when I get this up and running). Ugh - so, if someone had the patience to read through this and has a clue as to what the problem might be, then I could sure use some help :)

Notice the strange lighting artifact.


No, I don't notice it. Can you be a little clearer exactly what you see is not what you expect? That would be more helpful for those of us who are not familiar with your project. I guess that maybe you don't expect one side or the other to be lit, but I'm not sure.

Phrases like "middle top corner" is kind of ambiguous too.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game


Notice the strange lighting artifact.


No, I don't notice it. Can you be a little clearer exactly what you see is not what you expect? That would be more helpful for those of us who are not familiar with your project. I guess that maybe you don't expect one side or the other to be lit, but I'm not sure.

Phrases like "middle top corner" is kind of ambiguous too.


I meant the black streak in the image that bisects the cube - though the artifact is not nearly as apparent as when you can move around. So here it is - a demo (it' has a bunch of extra weight that you won't see on screen - I simply couldn't be bothered to remove all the dependencies). In any case - the scene is on the lower right in the window: click the Tab key to lock/unlock camera input: use the arrow keys to move the camera and the mouse to look around (try moving around the scene to get a full view of the problem). If it crashes or doesn't work for some reason, please let me know what error it pops up (it should do so if there's a problem).

Download cliky</a>

PS - ignore the shader errors that show up in the console window regarding the unfound uniforms - they're another dead weight I couldn't be bothered to remove :)

EDIT: dropped the CG code to lose the required DLL's and made the camera non-laptop friendly. Testing your code on a touchpad can make you forget that not everybody has one :)
Bumping once for good measure as I've gone over my code a few times and I'm still not sure what might be wrong.
I can't get it to run this on a pc I have with winxp. It tells me it's an invalid win32 exe.

whats the purpose of the 'if' condition for your 'normal'?

when you draw the normal buffer you have:
gl_FragData[2] = vec4(normal.xyz, 1);

but then when you try and get them in the final pass you have:
texture2D(tImage2, gl_TexCoord[0].xy) - 0.5

wouldn't the 0.5 throw the it off? I could be wrong and had missed something.

I can't get it to run this on a pc I have with winxp. It tells me it's an invalid win32 exe.

whats the purpose of the 'if' condition for your 'normal'?

when you draw the normal buffer you have:
gl_FragData[2] = vec4(normal.xyz, 1);

but then when you try and get them in the final pass you have:
texture2D(tImage2, gl_TexCoord[0].xy) - 0.5

wouldn't the 0.5 throw the it off? I could be wrong and had missed something.


Thanks for pointing that out! Looks like a typo - I'll have to check on it though.

As for the exe not running - I think I forgot to mention that it's 64-bit... :). I ported my whole code a while ago and haven't really seen a reason to switch back till now. I'll try to up a 32-bit build later tonight.

This topic is closed to new replies.

Advertisement