PDA

View Full Version : Shadow mapping, bias before the w divide ?!



opengeler
03-11-2011, 09:29 PM
Hello all,

I am currently following the tutorial from Fabien Sanglard:
http://fabiensanglard.net/shadowmapping/index.php

I pretty much understand everything except the bias involved when passing the light transformation to the shader:


void setTextureMatrix(void)
{
static double modelView[16];
static double projection[16];

// Moving from unit cube [-1,1] to [0,1]
const GLdouble bias[16] = {
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0};

// Grab modelview and transformation matrices
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);


glMatrixMode(GL_TEXTURE);
glActiveTextureARB(GL_TEXTURE7);

glLoadIdentity();
glLoadMatrixd(bias);

// concatating all matrices into one.
glMultMatrixd (projection);
glMultMatrixd (modelView);

// Go back to normal matrix mode
glMatrixMode(GL_MODELVIEW);
}


The fragment shader does something like:



// Vertex shader uses the matrix built in the code:
//ShadowCoord= gl_TextureMatrix[7] * gl_Vertex;
vec4 shadowCoordinateWdivide = ShadowCoord / ShadowCoord.w ;

// Used to lower moiré pattern and self-shadowing
shadowCoordinateWdivide.z += 0.0005;


float distanceFromLight = texture2D(ShadowMap,shadowCoordinateWdivide.st).z;



It seems that the viewport scaling is performed before the w divide and I don't get it, the viewport transform should be done after the w divide.
I have tried to remove the bias and do it after the w divide in the shader:

In the code:




//glLoadMatrixd(bias);



In the shader:



vec4 shadowCoordinateWdivide = ShadowCoord/ShadowCoord.w;
shadowCoordinateWdivide = shadowCoordinateWdivide *0.5+0.5 ;



I still get the shadow by doing the view port after the w divide (as expected).

Now I've done it on paper and to perform the viewport transform before the w divide doesn't produce the same result as doing the w divide and then the viewport transform.

I wonder if the graphic result is "OK" looking but mathematically incorrect in this tutorial.

remdul
03-15-2011, 08:28 AM
Bump. I followed the same tutorial and also had issues with bias. I ended up using polygon offset, which isn't a good solution because it can leave 1px gaps at grazing angles.

dukey
03-15-2011, 09:15 AM
Polygon offset is a real bad solution, because it'll work on your graphics card, you switch over to ATI or Intel or whatever, and you'll get entirely different results ..

remdul
03-15-2011, 09:26 AM
Polygon offset seems more consistent for orthographic projections, but indeed, another downside is that implementations may vary between vendors.

*Edit: there is an argument to be made for polygon offset though. Read this:
http://hacksoflife.blogspot.com/2009/01/polygon-offset-and-shadow-mapping.html
So what's the GLSL code equivalent to glPolygonOffset?

Dark Photon
03-16-2011, 05:50 AM
I ended up using polygon offset, which isn't a good solution because it can leave 1px gaps at grazing angles.
Another bias solution touted alongside glPolygonOffset (which can suffer depth blow-ups on tangents and allegedly can hurt/kill ZCULL/Hi-Z perf), is tweaking your projection matrix to do a depth offset:

* http://www.gamedev.net/topic/335012-glpo...08#entry3178508 (http://www.gamedev.net/topic/335012-glpolygonoffset-whats-the-deal-here-is-it-good-or-bad/page__p__3178508#entry3178508)
* http://www.terathon.com/books/code/Listing9.1.txt

Have read about it but not implemented it, as PolygonOffset works well enough here without any significant perf loss.

opengeler
03-16-2011, 02:46 PM
I understand that polygon offset allows to tweak the z generated and attempts to solve z-figthing....but I don't think the bias matrix I mention is trying to achieve this.

It really looks like a [-1,1] -> [0,1] thing....which doesn't make sense to me.



/ Moving from unit cube [-1,1] to [0,1]
const GLdouble bias[16] = {
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0};

Dark Photon
03-16-2011, 05:18 PM
I understand that polygon offset allows to tweak the z generated and attempts to solve z-figthing....but I don't think the bias matrix I mention is trying to achieve this.
Yes, you're right. And your interpretation...:


It really looks like a [-1,1] -> [0,1] thing....which doesn't make sense to me.
is spot-on. This (effectively) converts a light's NDC-space X,Y coordinate (-1..1, -1..1) into (0..1, 0..1) for use as a texture coordinate for shadow map texture lookup. It truth it operates in clip-space before the perspective divide, but that doesn't really matter. (x*0.5+w*0.5)/w = 0.5*x/w+0.5

This "* 0.5 + 0.5" bias matrix has nothing to do with reducing shadow map acne. I think remdul misinterpretted the meaning of bias here, and then we all chimed in with techniques for reducing shadow map acne. :) Sorry about that.

Bias alone in shadow map context is ambiguous. Often refers to acne reduction "bias", but in this context is also used as the name of the NDC-to-texcoord transform.

Dark Photon
03-16-2011, 05:45 PM
Just to clarify:

1. Applying bias transform before perspective divide (i.e. in CLIP SPACE):



(x,y,z,w) * bias = (0.5*x + 0.5*w, ..., ..., w)
Then perspective divide = ( 0.5*x/w + 0.5, ..., ... )


2. Applying bias transform "after" perspective divide (i.e. in NDC SPACE):



(x,y,z,w) -> Perspective divide = (x/w, y/w, z/w)
Then apply bias transform = (0.5*x/w + 0.5, ..., ... )