3
\$\begingroup\$

I need to avoid z-fighting in exactly co-planar surfaces that are too close to the other solids. I'm re-implementing the Freescape engine in ScummVM (all my code is open-source, available here), and this how it looks when rendering all the shapes in a naive way (the door on the right is barely visible):

z-fighting on Driller

(side note: if you recognize the scene, this is the initial level of Driller, released in 1987. It was a predecessor of FPS games).

The freescape engine games are very prone to this z-fighting when implemented using z-buffer since a good number of planar surfaces are placed one unit away from other non-planar solids (e.g. cubes or pyramids).

I tried a few of the usual solution, including:

  • Tweaking znear and zfar values: these improved a little, but essentially the problem is still there.
  • Adding random small displacements to separate surfaces: some of the levels in freescape games rely on the clever usage of planar surfaces to draw things (e.g a Sphinx). Slightly moving planar surfaces break the symmetry so it is not an option.
  • I tried some suggestions from StackOverflow, for instance, this one but it seems that drawing using the depth testing only will still produce z-fighting.

So far, I was using glPolygonOffset before rendering planar surfaces to mitigate this; that was a reasonable workaround (but still, far from perfect). You can see the code here:

void OpenGLRenderer::polygonOffset(bool enabled) {
    if (enabled) {
     glEnable(GL_POLYGON_OFFSET_FILL);
     glPolygonOffset(-10.0f, 1.0f);
    } else {
     glPolygonOffset(0, 0);
     glDisable(GL_POLYGON_OFFSET_FILL);
    }
}

The result looks like this:

z-fighting mitigation

It is generally ok. However, it is not hard to notice z-fighting at the distance, in certain angles. This is much worse in later games. I need to know which is the best approach for solving this issue, once and for all.

z-fighting fighting back

Fortunately, this ScummVM engine aims to recreate freescape games, which had some constraints in their levels. I did some research on the original freescape renderer, which used some kind of bounding box sorting for rendering. In practice, this means that all the elements in the design can be sorted by axis and they never overlap (so the fix does not have to be general, just enough to render the levels correctly).

I don't want to reimplement the exact same approach, for several reasons:

  • Z-buffer is very fast and easy to use in OpenGL. It already works fine for 95% of the graphics on the game. It is also much easier to maintain than my own depth buffer implementation.
  • The original implementation game had notable visual artifacts that I suspect that some of them could be related with the way this worked.
  • I have almost no experience in computer graphics and it looks hard.

One important implementation detail: I'm aiming to support three renderers: TinyGL (software, but still following the OpenGL API), old OpenGL with fixed-functions and modern OpenGL using shaders. If you are wondering why we try to support fixed-function OpenGL, the reason is that ScummVM aims to run on old devices that do not always allow modern versions of OpenGL.

For the OpenGL fixed-function renderer, I think the ideal solution will be something like changing the order/approach in which planar surfaces are rendered, but in way that the depth information is preserved. I tried this:

gfx->depthBuffer(false);

for (auto &planar : planarObjects)
    nonPlanar->draw(gfx);

gfx->depthBuffer(true);

for (auto &nonPlanar : nonPlanarObjects)
    nonPlanar->draw(gfx);

This completely avoid z-fighting, but as expected, the depth information of the planar shapes is not preserved at all, so it is not a good solution. Using glDepthMask will not work as well, for some reason.

So, which recommendations do you have for the fixed-function and the shader OpenGL renderers?

\$\endgroup\$
5
  • \$\begingroup\$ Make sure to have Near and Far planes set at reasonable distances. \$\endgroup\$
    – Kromster
    Commented May 1 at 8:18
  • \$\begingroup\$ The z-fighting is still there when looking directly at the plane (e.g. pitch == 0), so I'm looking for a better option. There is even a specific workaround for that, but it is still not enough to avoid z-fighting on large distances (e.g. a long corridor). \$\endgroup\$ Commented May 1 at 8:26
  • \$\begingroup\$ Not an answer, but you might to search the Q&A for z fighting on the computer graphics stack exchange - in particular Avoiding z-fighting with coincident surfaces. \$\endgroup\$
    – Pikalek
    Commented May 1 at 14:17
  • \$\begingroup\$ I played that game on the C64 at about 2 FPS. :D \$\endgroup\$
    – Almo
    Commented May 2 at 15:52
  • \$\begingroup\$ You can now replay it at 120 FPS! \$\endgroup\$ Commented May 2 at 17:54

0

You must log in to answer this question.

Browse other questions tagged .