26
$\begingroup$

When rendering two overlapping co-planar surfaces, a common issue is "z-fighting", where the renderer can't decide which of the two surfaces is closer to the camera, giving visual artifacts in the area of overlap.

The standard solution to this is to give the surfaces a slight offset when designing the model. Is there any other solution?

$\endgroup$
  • 2
    $\begingroup$ You could checkout logarithmic depth buffers. There's an article on gamasutra $\endgroup$ – Soapy Aug 7 '15 at 8:12
  • 1
    $\begingroup$ When you say "co-planar" do you mean "nearly" or "exactly" co-planar and if the latter, are those surfaces ever identical surfaces/triangles? The rendering hardware should be deterministic (assuming you aren't submitting in random order) for the last case and not have fighting. If it's a case of non-identical surfaces but exactly co-planar, could you update the model to split surfaces into overlapping and non-overlapping portions? $\endgroup$ – Simon F Aug 7 '15 at 12:55
  • $\begingroup$ @SimonF, by "co-planar" I mean "exactly co-planar". Soapy's solution only works in the "nearly co-planar" case. $\endgroup$ – Mark Aug 7 '15 at 18:38
  • $\begingroup$ Could you give an example of your surfaces? The only thing I can think of off the top of my head is duplicate triangles as @SimonF mentioned. $\endgroup$ – RichieSams Aug 7 '15 at 20:28
  • $\begingroup$ @RichieSams the most common case that I can think of is decals, where you don't need exactly duplicate triangles. $\endgroup$ – rys Aug 7 '15 at 23:15
10
$\begingroup$

If the surfaces are exactly co-planer, your fate is up to the FPU gods; you'll more than likely have Z-fighting. If the triangles are identical and you do the exact same math to each, you will end up with the same Z-values for both. But again, this will only happen if your math operations are identical for both. (Since, in general, FPU operations are not commutative)

One possible solution is to not use the Z-buffer at all. Rather you can use the Painter's Algorithm. Granted, this comes with all the problems of the painters algorithm as well. But, it would solve the Z-fighting.

In your example case of Screen Space Decals (SSDs), the common solution is to use an offset, aka a simple thin cube. See Warhammer 40k's presentation about SSDs for reference. Or Bart Wronski's post which addresses some other issues with decals, but also links to a few other presentations about SSDs

$\endgroup$
  • 5
    $\begingroup$ Re-rendering the same geometry with the same transforms does reliably generate the same depth values every time. (I.e. it's not a might, it's a will). That's why multi-pass forward lighting works, for instance. $\endgroup$ – Nathan Reed Aug 8 '15 at 17:51
  • $\begingroup$ @NathanReed Corrected. Thank you for the clarification $\endgroup$ – RichieSams Aug 9 '15 at 18:44
  • 1
    $\begingroup$ To get that functionality you need to use the invariant qualifier in glsl: opengl.org/wiki/Type_Qualifier_%28GLSL%29#Invariance_qualifiers $\endgroup$ – ashleysmithgpu Aug 10 '15 at 11:11
  • $\begingroup$ Note that identical shader expressions (and inputs, obviously) when evaluating vertex positions may not be enough to get identical results as some optimisations may depend on the rest of the shader. GLSL has the "invariant" keyword to declare shader outputs that must be evaluated identically in different shaders. $\endgroup$ – Firadeoclus Aug 10 '15 at 11:13
2
$\begingroup$

Here's how I've solved this in the past:

  1. Draw the first object (the one that should appear behind the other object) by depth testing but not depth writing
  2. Draw the second object by depth testing and depth writing. This won't cause z-fighting since we didn't write any depth in step 1.
  3. Draw the first object by only writing to the depth buffer and not to the color buffer. This makes sure the depth buffer is up to date for any pixels that are covered by object 1 but not by object 2.

Note, the objects need to be drawn consecutively for this to work.

$\endgroup$
0
$\begingroup$

Depending on your renderer you could make the surfaces more "fuzzy" applying a miniscule offset in multisampling depth using a noise like algorithm. This should result in a sort of blending effect for surfaces in close depth proximity. For what I know setting per-sample depth in fragment shader is quite recent in OpenGL and then only as an extension.

OpenGL has the PolygonOffset but requires knowing in advance you are about to render something coplanar. While not feasible for surfaces within a single model it could work nicely for example when rendering a road overlaid on a piece of land.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.