Shadow mapping, bias before the w divide ?!

Hello all,

I am currently following the tutorial from Fabien Sanglard:

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);
		// concatating all matrices into one.
		glMultMatrixd (projection);
		glMultMatrixd (modelView);
		// Go back to normal matrix mode

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,;

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:


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.

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.

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 …

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:
So what’s the GLSL code equivalent to glPolygonOffset?

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:

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

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};

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. (x0.5+w0.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. :slight_smile: 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.

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, ..., ... )

  1. 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, ..., ... )