Shadow Volume Flickering

Started by
12 comments, last by RPTD 18 years, 7 months ago
i've implemented the shadow volume algorithme after the good tutorial on gamasutra. now my problem is that while the algorithme works well on distance i get disturbing results if getting close to a model. if i am at medium distance the shadow is correct. if i get closer to the model the shadows start to 'fade' away or 'pop' up on the model. it looks to me like the z-buffer values used during shadowing and rendering are not the same. for rendering the shadow volume and rendering i use an infinite matrix using this:
decMatrix decMatrix::CreateProjectionInfinite(int width, int height, float fov, float fovRatio, float znear){
	if( width<1 || height<1 || fov<=0 || fov>=PI || fovRatio==0 ) THROW(dueInvalidParam);
	decMatrix m;
	float a = (float)width / (float)height;
	float fx = 1.0f / tan(fov * 0.5f);
	float fy = a / tan(fov * fovRatio * 0.5f);
	float e = 0.0001f;
	m.a11=fx; m.a12=0;  m.a13=0;   m.a14=0;
	m.a21=0;  m.a22=fy; m.a23=0;   m.a24=0;
	m.a31=0;  m.a32=0;  m.a33=1-e; m.a34=znear*(e-2);
	m.a41=0;  m.a42=0;  m.a43=1;   m.a44=0;
	return m;
}
multiplied with a small translation matrix (translate z around 0.0001) to avoid z-fighting. how can it happen that getting close to objects in my scene (funny not all of them) show this strange behaviour? is the z-offset not good? or is the matrix not fully correct?

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

Advertisement
Quote:Original post by RPTD
multiplied with a small translation matrix (translate z around 0.0001) to avoid z-fighting.


Excuse me, but why do you think that any z-fighting will occur in this case? Do you render different geometry/apply different vertex shaders when you are doing the second pass?
You shouldn't need the translation matrix. Just render each geometry pass with the same exact vertices and use LEQUAL as your depth function, and you should be fine.

Which API are you using, OpenGL or DirectX? I think your infinite projection matrix may need an adjustment, but it depends on the API. (The matrix in the gamasutra article is correct for OpenGL.)

-- Eric Lengyel
Quote:Original post by Generic Guest
Quote:Original post by RPTD
multiplied with a small translation matrix (translate z around 0.0001) to avoid z-fighting.


Excuse me, but why do you think that any z-fighting will occur in this case? Do you render different geometry/apply different vertex shaders when you are doing the second pass?

i'm using this anti-z-flickering as if i am not using it all shadows on surfaces are flickering like hell. with that small fix i got all shadows working except this case with the model very close to the camera.

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

Quote:Original post by Eric Lengyel
You shouldn't need the translation matrix. Just render each geometry pass with the same exact vertices and use LEQUAL as your depth function, and you should be fine.

Which API are you using, OpenGL or DirectX? I think your infinite projection matrix may need an adjustment, but it depends on the API. (The matrix in the gamasutra article is correct for OpenGL.)

-- Eric Lengyel

i'm using opengl on linux on a ati radeon. and my matrix implementation inside my engine is a directx-like matrix which i convert to opengl format upon setting it (special render module system to separate engine and os).

about the LEQUAL, i'm using this before shadowing.
			// setup stuff for shadow rendering			OGL_CHECK( glClear(GL_STENCIL_BUFFER_BIT) );			OGL_CHECK( glEnable(GL_STENCIL_TEST) );			OGL_CHECK( glStencilFunc(GL_ALWAYS, 0, ~0) );			OGL_CHECK( glEnable(GL_DEPTH_TEST) );			OGL_CHECK( glDepthFunc(GL_LESS) );			OGL_CHECK( glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) );			OGL_CHECK( glDepthMask(GL_FALSE) );			pSetMatrix( GL_PROJECTION, zSafeProjMat );


this is what i took out of the gamasutra article unless i understood something wrong there.

shadowing itself i do like this (i think this should be correct):
void deoglShadowVolume::RenderShadows(deGraphicOpenGl *ogl, deoglMeshData *mesh, bool renderCaps){	// first pass	if(renderCaps){		OGL_CHECK( glCullFace(GL_FRONT) );		OGL_CHECK( glStencilOp(GL_KEEP, GL_INCR_WRAP_EXT, GL_KEEP) );	}else{		OGL_CHECK( glCullFace(GL_BACK) );		OGL_CHECK( glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT) );	}	pRenderShadowPass(mesh, renderCaps);	// second pass	if(renderCaps){		OGL_CHECK( glCullFace(GL_BACK) );		OGL_CHECK( glStencilOp(GL_KEEP, GL_DECR_WRAP_EXT, GL_KEEP) );	}else{		OGL_CHECK( glCullFace(GL_FRONT) );		OGL_CHECK( glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT) );	}	pRenderShadowPass(mesh, renderCaps);}

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

using LEQUAL and leaving out the anti-z-fight matrix yields z-fighting, like seen here:


can it be that the matrix is wrong? i see nothing wrong there though.

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

i managed to narrow down the problem. it is not the perspective matrix. if rendering Depth-pass without caps i have no flickering, if i render all in Depth-fail with caps though it flickers like hell.

altering the 'e' value in the matrix i posted doesn't solve the problem hence i don't think the far clipping plane is the bad guy. i assume it is the near clipping plane but from my current understanding of this technic i don't get how this can be a problem with Depth-fail. somebody can give me some pointers or knows what problem i have run into?

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

The blockiness in your image has driver bug written all over it.

For the caps, are you sure that the vertex program is producing the exact same vertex positions that are used in the shadow extrusion and the mesh itself? Can you post your vertex programs/shaders for the extrusion and caps?

Also, this is a long shot, but you wouldn't happen to have GL_POINT_SPRITE_ARB or GL_VERTEX_PROGRAM_POINT_SIZE_ARB enabled? I've noticed similar blockiness problems on ATI hardware in the past with these enabled (even though they have nothing to do with what you're rendering).
about the caps i don't get exactly what you are refering too, but it should always use the same vertices.

this vertex-program is used to project the shadow volume:
!!ARBvp1.0## OpenGL Graphic Module Vertex Program## Copyright (C) 2004, Plüss Roland ( rptd@gmx.net )# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later # version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.# # You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.## input data##############ATTRIB inPos = vertex.position;					# position object space# output data###############OUTPUT outHPos = result.position;				# homogenous position# constants############## eyePARAM pEyePos = program.local[0];	# eye position in object space# lightPARAM pLightPos = program.local[1];	# object spacePARAM pLightDir = program.local[2];	# object spacePARAM pLightColor = program.local[3];PARAM pLightOptions = program.local[4];	# type, power, cutoff, spotexp# opengl states#################PARAM pMatMVP[4] = { state.matrix.mvp };# variables#############TEMP t1;# transform the vertex with the matrix## t1 = inPos.w * pLightPos + (inPos - pLightPos, 0)ADD t1.xyz, inPos, -pLightPos;MOV t1.w, 0;MAD t1, pLightPos, inPos.w, t1;## outHPos = oglMatProj * (oglMatMdl * inPos);DP4 outHPos.x, t1, pMatMVP[0];DP4 outHPos.y, t1, pMatMVP[1];DP4 outHPos.z, t1, pMatMVP[2];DP4 outHPos.w, t1, pMatMVP[3];# end of program# 4 instructions# 1 temp registersEND


this one used for the mesh (it's the ambient shader but the light shaders are all the same except some additional parameters calculated):

!!ARBvp1.0## OpenGL Graphic Module Vertex Program## Copyright (C) 2004, Plüss Roland ( rptd@gmx.net )# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later # version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.# # You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.## input data##############ATTRIB inPos = vertex.position;					# position object spaceATTRIB inColor = vertex.color.primary;			# ambient + emissiveATTRIB inTexPos = vertex.texcoord[0];			# texture coordinates# output data###############OUTPUT outHPos = result.position;				# homogenous positionOUTPUT outColor = result.color.front.primary;	# ambient + emissiveOUTPUT outTexPos = result.texcoord[0];			# texture coordinates# constants############## opengl states#################PARAM pMatTex1[4] = { state.matrix.texture[0] };PARAM pMatMVP[4] = { state.matrix.mvp };# variables############## calculate texture coordinates## outTexPos = oglMatTex1 * inTexPosDPH outTexPos.x, inTexPos, pMatTex1[0];DPH outTexPos.y, inTexPos, pMatTex1[1];# color is ambient + emissive## outColor = ambient + emissive;MOV outColor, inColor;# transform the vertex with the matrix## outHPos = oglMatProj * (oglMatMdl * inPos);DPH outHPos.x, inPos, pMatMVP[0];DPH outHPos.y, inPos, pMatMVP[1];DPH outHPos.z, inPos, pMatMVP[2];DPH outHPos.w, inPos, pMatMVP[3];# end of program# 7 instructions# 0 temp registersEND



about the extensions i'm not using them but it may be well the driver as i have also witnessed once texture-compression bugs where the driver compressed textures which he should not without asking me.

Life's like a Hydra... cut off one problem just to have two more popping out.
Leader and Coder: Project Epsylon | Drag[en]gine Game Engine

Try changing your extrusion code to the following:

# transform the vertex with the matrix## t1.xyz = (inPos.w < 0.5) ? inPos - pLightPos : inPos;## t1.w = inPos.w;SLT t1.w, inPos.w, 0.5;MAD t1.xyz, pLightPos, -t1.w, inPos;MOV t1.w, inPos.w;## outHPos = oglMatProj * (oglMatMdl * inPos);DP4 outHPos.x, t1, pMatMVP[0];DP4 outHPos.y, t1, pMatMVP[1];DP4 outHPos.z, t1, pMatMVP[2];DP4 outHPos.w, t1, pMatMVP[3];


I've found that some processors with lower floating-point precision can cause problems when you try to do a + b - a, as I suggested in the gamasutra article. (At the time, it worked fine on all available GPUs, but on some newer GPUs you don't necessarily get exactly b back.) The above code is guaranteed to preserve your vertex position if its w component is 1.

This topic is closed to new replies.

Advertisement