View Full Version : z-fighting

01-12-2002, 05:23 AM

I'm rendering an underwater landscape in two passes.
The first pass is the simple landscape, while the second is the caustic textures over the landscape.
The problem is that in 16 bit mode everything is fine, in 32 bit mode, there is a mess of z-fighting.

How can i fix it ?


01-12-2002, 06:11 AM
Push the near clip plane further away from the viewpoint.

01-12-2002, 06:46 AM
Or use the stencil buffer to determine where the second layer should be rendered, rather than the depth buffer.

01-12-2002, 09:44 AM
Thanks fellows, but, that won't help.
Why would i need to move the znear clip plane if it works in 16 bit mode ?
And what is interesting is that in my geforce3 it works great in both modes, on the geforce2 only works in 16 bit mode, in 32 bit mode it fails.....,
any ideias ?


01-12-2002, 09:56 AM
Hmm, think I read your question a bit wrong. Do you render the terrain in the first pass, and a second texture ontop of the terrain in a second pass? I thought the second pass was some kind of water plane, and you got Z-fighting in the intersection between this plane and the terrain.

Anyways. It sounds like you indeed have Z-fighting, but in 16 bit mode it's so much that it looks good (but isn't). Sounds strange, but I can imagine such a situation.

Does the two passes have identical geometry? That is, do you do anything that can possibly affect the coordinates? For example, doing a translation, and then translation back with negative values does not guarantee identical coordinates.

A screenshot of the problem would be great. If you can't upload it somewhere, send be a shot by mail (address in profile).

A tip can be to use polygon offset.

01-12-2002, 11:19 AM
I have uploaded 2 screenshots.
The shot were you can see the z-fighting is this one : http://brunomtc.no.sapo.pt/messedZ.JPG
It was taken on a gf2 while in window mode.
The correct one, is here: http://brunomtc.no.sapo.pt/FixZ.jpg
It was taken on the gf3 in window mode too.
I couldn't take the good shot on the gf2, because it's having z-figthing on window mode too.., so, on the gf2 the only mode that works is fullscreen 16 bit.


01-12-2002, 11:23 AM
I forgot http://www.opengl.org/discussion_boards/ubb/smile.gif
Yes, the landscape is in the same place, i just render it again without anything that could affect the vertices.


01-12-2002, 11:35 AM
Are you rendering the landscape polys in the same order during second pass? And are you using a depth test of GL_EQUAL on the second pass?

01-12-2002, 11:37 AM
use glPolygonOffset(..)

01-12-2002, 12:24 PM
Any difference with 24-bit z-buffer depth?

01-12-2002, 12:32 PM
If you increase the color depth, depending on your video card, the z-buffer depth (bitwise) will decrease, thus resulting in less precise values, and that can cause z-fighting.

01-12-2002, 11:21 PM
Ok, i think i fixed it like this :

glDepthRange (0, 0.5);
Render Landscape
glDepthRange (0, 1);

thanks guys,

01-13-2002, 01:14 AM
No, you haven't solved this. Your landscape will not depth buffer correctly with other stuff. I dunno why this even worked unless it's because your caustics are not drawn with your landscape, in which case the caustics will 'punch through' both the terrain and other stuff in the scene under a lot of circumstances.

Use glPolygonOffset for this, unfortunately various OpenGL implementors have made a right royal mess of this feature because they can't read a spec and the glPolygonOffsetEXT confused them. The conformance test is also a bag of bollocks (and still is despite my protestations). Now we may even be in a legacy situation where fixing implementations might break apps. Just experiment with a range of values, and test on different hardware if you can (and curse under your breath that you have to live with the mess).

Here's a decent demo of this effect with a good animated texture you might want to 'borrow', everyone does, it's owned by Jos Stam but he's been generous with permission for it's use in the past.

Depth buffer offsets are a tradeoff between excessive punchthrough and sufficient offset to avoid z fighting. glPolygonOffset when implemented correctly is designed to be the perfect mechanism to reach the ideal compromise.

[This message has been edited by dorbie (edited 01-13-2002).]

01-13-2002, 02:05 AM
Why would you need glPolygonOffset for such a technique ? It's just a simple multipass effect and rendering the second pass with glDepthFunc( GL_EQUAL ) should do it.

Drawing the exact same triangles should produce the exact same depth values ( according to the GL specs ).

Even the demo that dorbie posted a link to uses glDepthFunc( GL_EQUAL ) for it's second pass and not glPolygonOffset. Did I miss something here ?

01-13-2002, 02:41 AM
You should test it with a simple draw triangle that start from the back and come to the front. No ?

It's true that the same triangles MUST return the same depth values... strange...


01-13-2002, 03:02 AM
PH, I'm not sure whether it still works with vertex programs. I don't think different vertex programs will produce the same exact result even if in theory they should.

Bruno, if you can apply both in one pass, use multitexturing.
Otherwise, solutions have been given: Poly Offset or Stencil Buffer.

01-13-2002, 03:13 AM
It can work in vertex programs. However, I've also heard people say that the order of operations in vertex programs can affect the value.

If all your HPOS values are calculated in the same way in all your different vertex programs, then there is no reason why you shouldn't get perfect multi-pass rendering without Zbuffer artifacts.

This will only become an issue if you calculate your HPOS values slightly differently, or at different places within the actual program for subsequent passes. Or if you mix vertex programs and fixed function pipelines in multipass. This will almost certainly result in the Z values of each pass being different.

I've done multipass on terrain, and never had any problems on any Geforce card, in 16bit and 32bit. You do _not_ need to use glPolygonOffset, for simple multipass.


01-13-2002, 03:14 AM
GPSnoopy, you can't mix vertex programs and ordinary passes, that's true. But different vertex programs can still be mixed ( if the exact same transformation is used for vertex positions).

01-13-2002, 03:54 AM
PH, gaby, GPSnoopy & Nutty,

in OpenGL you are not guaranteed exact Z fragment reproduction if you change the pipeline path (set of state). You might be OK, but another OpenGL implementation may trip you up later, and if the implementation you are using today is showing a problem then you KNOW that you need to fix it. BLENDING to add a caustic effect on a second pass is potentially the kind of thing that might mess this up on some implementations.

OTOH, this may not be the cause, but I know what the solution is, see my other post.

[This message has been edited by dorbie (edited 01-13-2002).]

01-13-2002, 04:27 AM

Ok, I see what you mean. I just had a quick look in the specs about invariance. I'm surprised blending and depth testing is not required for fragment generation to be invariant but only strongly suggested ( that's how I interpreted it, that's what you mean, right ? ).


01-13-2002, 04:35 AM

01-13-2002, 10:10 AM
as dorbie + i said polygonoffset is needed else youre gonna get zbuffer fighting
this brings me to my own question about caustics do caustics appear on the waters surface ie is the second shot correct or not (i have the feeling they dont) http://uk.geocities.com/sloppyturds/caustics1.jpg http://uk.geocities.com/sloppyturds/caustics2.jpg

01-13-2002, 10:17 AM
No, they dont appear on the surface of the water. What you do get though, is refraction, which undulates with the waves.

I'm still not convinced that multipass requires glPolygonOffset. I've done a helluva lot of multipass on geforce cards, and I have never had any Z fighting.

The only time I ever did, was using vertex programs for 1 pass, and fixed function for the 2nd pass.


01-13-2002, 10:29 AM
I agree with Nutty, here. Come on, even old games have used multipass effects without glPolygonOffset - maybe someone should tell Carmack that he better use glPolygonOffset in id Software's new DOOM game ( and fix all the Quake games ).

Using glPolygonOffset for multipass effects MAY be required for some weird states ( on some weird old hardware ) but ordinarily it won't be required.

01-13-2002, 10:50 AM
For multipass, I just use GL_EQUAL.

I only use glPolysonOffset when rendering 2 "different objects" that are really closed.

I always though that was how it was supposed to used.

[This message has been edited by Gorg (edited 01-13-2002).]

01-13-2002, 11:44 AM
"Appendix H: OpenGL Invariance" in The Redbook has some good infomation on this - see the second last paragraph,

01-13-2002, 02:16 PM
Ok.., i found an interesting thing.
I'm using a clipplane to clip the landscape that is above the water level., so i only have caustics below the water.
The interesting is that if i disable the clipplane i have a wonderful scene with no z-fighting..., Why ? Why a clipplane does this ?


01-13-2002, 09:42 PM
PH, that is the test, not the fragment values which are created. There is a deliberate opt out clause in the spec for this. You can look for evidence to the contrary but it won't change the spec.

Just look at Bruno and his clip plane, that screwed up his depth fragments, possibly on triangles that weren't even clipped, but perhaps they were. Other things can cause similar problems, and worst of all changing hardware platforms can have an effect you do not anticipate.

01-13-2002, 09:48 PM

clipping the geometry introduces rounding errors which probably produces different z fragments on clipped geometry. The solution is still glPolygonOffset. As I said earlier, the suspected problem may not be the culprit, but I know what the solution is.

There is an alternative solution. When you clear the screen clear the stencil buffer to 0. Then when you draw the water surface write a stencil value of 1 on a depth pass, next draw the caustics but test against stencil == 0. The benefit is you can have non planar water and still only write to submerged fragments.

Hmm... thinking about this, if you draw the water before the caustics a simple depth test will prevent caustics above the water, so turn off clipping, and just draw terrain, then water then caustics and it will work when you are below the water.

[This message has been edited by dorbie (edited 01-13-2002).]

01-13-2002, 11:34 PM
Yes introducing a clip plane on the 2nd pass, is a huge state change, and is most definitly the reason for your Z fighting.

01-13-2002, 11:54 PM
what I have found, in my limited experience, is that I can get z fighting with exactly the same geometry using GL_EQUAL (although I almost always use GL_LEQUAL)

the only time this occured was when I was passing the geometry using glVertex calls.. but I found the problem dissapeared completly when using arrays adn drawelements...
(this was a very long time ago, so I don't have the buggy code that did it...)

I've also noticed with shadow volumes, using glVertex I get the odd stream running down the shadow, yet with compiled arrays I don't... but once again. that was a while back. (which is a similar problem I guess)

01-14-2002, 12:13 AM
You used a GL_EQUAL depth test in a multipass shader? I salute your heroic optimism.

01-14-2002, 05:20 AM
Originally posted by dorbie:
PH, that is the test, not the fragment values which are created. There is a deliberate opt out clause in the spec for this. You can look for evidence to the contrary but it won't change the spec.

Yes, you are right. I mixed it up with fragment generation.

User clip planes usually screw up multipass rendering - most of the time I use textures for implementing clip planes ( that should solve Bruno's problem ). I've never had z-fighting in my own work ( even on a Voodoo3 with 6-pass shaders ).