Dealing with z-fighting within the same mesh

For GUI work, I use a single mesh and layer my gui components into it using the vertex buffers, i.e. each new image has a new Position and TexCoords, and incremented Indexes. These images are all co-planar, but when the mesh precisely faces the camera (as a GUI typically does) this isn’t a problem: high index vertexes are always drawn over the top of low index vertexes.

I’ve started using this GUI mesh within the 3d environment though (e.g. integrated into a wall), but since the mesh doesn’t always directly face the camera, my GUI components have started z-fighting.

Here is a simple demo. There is no Z-fighting unless you move the mouse.
public class GuiMeshTest extends SimpleApplication {

  public static void main(String[] args) {
    GuiMeshTest test = new GuiMeshTest();
    test.setSettings(new AppSettings(true));
    test.start();
  }

  @Override
  public void simpleInitApp() {
    assetManager.registerLocator("assets", FileLocator.class);
    getStateManager().getState(FlyCamAppState.class).getCamera().setMoveSpeed(50f);

    GuiMesh guiMesh = new GuiMesh();
    guiMesh.addSprite(0, 0, 3, 3);
    guiMesh.addSprite(2, 0, 3, 3);
    guiMesh.updateBuffers();

    Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
    Geometry geometry = new Geometry("test", guiMesh);
    geometry.setMaterial(material);
    rootNode.attachChild(geometry);
  }

  private static class GuiMesh extends Mesh {

    private final int maxSprites = 2;
    private int spriteIndex = 0;

    private float[] positions = new float[maxSprites * 8];
    private float[] texCoords = new float[maxSprites * 8];
    private short[] indexes = new short[maxSprites * 6];

    public void addSprite(float x, float y, float width, float height) {
      float[] positions = new float[] { x, y, x + width, y, x + width, y + height, x, y + height };
      float[] texCoords = new float[] { 0, 0, 1, 0, 1, 1, 0, 1 };
      short[] indexes = new short[] { 0, 1, 2, 0, 2, 3 };
      for (int i = 0; i != indexes.length; i++)
        indexes[i] += spriteIndex * 4;

      System.arraycopy(positions, 0, this.positions, spriteIndex * positions.length, positions.length);
      System.arraycopy(texCoords, 0, this.texCoords, spriteIndex * texCoords.length, texCoords.length);
      System.arraycopy(indexes, 0, this.indexes, spriteIndex * indexes.length, indexes.length);

      spriteIndex++;
    }

    public void updateBuffers() {
      setBuffer(Type.Position, 2, positions);
      setBuffer(Type.TexCoord, 2, texCoords);
      setBuffer(Type.Index, 3, indexes);
      updateCounts();
      updateBound();
    }

  }

}

What is the appropriate solution to this?

Have you considered using @pspeed’s Lemur library?

Thanks, I haven’t considered it and I’ll look into it to see if it can replace/enhance what I have.

To be clear though, I already have lots of functional GUI code and that isn’t really the issue. It’s just that what I have is breaking now that I’m trying to take it out of it’s 2d shell and integrate it into the 3d world.

Any chance of a screenshot? It’s not immediately obvious to me just from the code and I’m not at my PC right now.

Sure thing.

Looking directly at the mesh there’s no problem, looking at it from an angle, there’s z-fighting.

1 Like

A mesh is basically a single draw call to the GPU. If you need the triangles rendered in a different order then you have to reorder them in the mesh.

Edit: and in your case where you want a layered 2D GUI in 3D space, you may have to render it to a texture and use that. There is no triangle ordering that will work in your case as it’s all down to Z-buffer precision.

You can add a BillBoard Control to the mesh to make it facing the camera whatever the position of the camera is

guimesh.addControl(new BillboardControl());

You could also turn of depth writing and things will just be rendered in the order of your mesh without any z-test, basically.

Is it possible to do something like set the DepthFunc to TestFunction.Always, except just while this geometry is rendering? Because that fixes the issue, but obviously causes other problems because it interferes with it’s depth compared to other meshes.

Edit: re above, how do I do that?

A mesh is a single draw call. The whole mesh can test depth or not test depth. Or the whole mesh can write depth or not write depth. That’s it.

There might be some trick to play with rendering an invisible quad or something. But ultimately your choices are limited.

Material.getAdditionalRenderatate.setDepthTest(false); or something like that.

So many people missing the actual issue…

No no I understand the issue. I think merging the textures is the solution.

I’m not on my PC but those 3 lines is how you create an image to render to a texture.

https://github.com/jayfella/jme-fastnoise/blob/master/src/main/java/com/jayfella/fastnoise/NoiseLayer.java#L123

It’s been a long time since I’ve played with zbuffer tricks. I seem to recall that depth testing has a dependency on depth writing.

…but if you could depth test without depth writing then you could in theory draw your gui only with depth testing and then render an invisible quad on top of it with regular test+write.

But I sort of half-remember that if you don’t depth write then the test function has nothing to compare.

In the end, as mentioned before, render to texture may be the only option for things to look perfect as a single mesh. If your GUI elements were separate meshes then you have lots of options.

1 Like

You could also turn of depth writing and things will just be rendered in the order of your mesh without any z-test, basically.

This seems to sort-of do the job! Shadows appear to be leaking through from behind but I might be able to work my way around that.

Otherwise, I guess yeah as suggested, I’ll keep rendering everything in 2d but into a new image or frame buffer or whatever, and then render that in the 3d environment.

Appreciate the help!

Another trick may be to render your GUI first before anything else with only depth write. Let everything else do the testing.

Doesn’t work with any transparency in your GUI, though.

1 Like

yes, do like you said. render as texture first, then apply. (remember clear flags for color to be able have it transparent)

this way you can even have GUI texture on some game-computer/game-item screen that is nice.

but you need care about FrustrumFar since if it will be too big, it might need more z-index differences to avoid z-fight.