Search code examples
javaopenglgraphicslwjgl

Unwanted triangles in OpenGL --- Z-fighting?


I am currently writing a small program using LWJGL to generate a low poly scene. I first rendered a house. Then I rendered the terrain. Lastly, I rendered the water. The water was originally a flat plane of vertices. I did some displacements in the vertex shader using the Perlin noise algorithm to generate some waves(i.e. I can't obtain the actual water level at a particular point). When I rendered the scene, I observed some serrated edges.

The situation is even worse when viewing from a larger distance. enter image description here

What are they? How can I remove them? Sorry that I can't upload the code. Because the number of lines are too large.

Edit 1:

private float FOV = 70;
private float NEAR_PLANE = .1f;
private float FAR_PLANE = 1000f;

private void createProjectionMatrix() {

    float aspectRatio = (float) Display.getWidth() / (float) Display.getHeight();
    float y_scale = (float) ( (1f/ Math.tan(FOV/2f))*aspectRatio );
    float x_scale = y_scale / aspectRatio;
    float frustum_length = FAR_PLANE - NEAR_PLANE;

    projectionMatrix = new Matrix4f();
    projectionMatrix.m00 = x_scale;
    projectionMatrix.m11 = y_scale;
    projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustum_length);
    projectionMatrix.m23 = -1;
    projectionMatrix.m32 = -((2*NEAR_PLANE * FAR_PLANE) / frustum_length);
    projectionMatrix.m33 = 0;

}

Solution

  • the problem is due to Z-buffer stored Z values of your fragments has low precision. Look at your values:

    private float NEAR_PLANE = .1f;
    private float FAR_PLANE = 1000f;
    

    so visible depth range is z = < 0.1 , 1000.0 > this is normalized to < 0.0 , 1.0> range. Now if your pixelformat uses only 16 bit Z-buffer then (assuming linear Z-buffer which is not the case) you got precision ~(1000.0/0.1)/2^16 = 0.6 which is smallest possible Z-step. Now if you take into account that the Z-buffer values are mapped non linearly then the more far away from your Z_Near the precision will be even less.

    To improve this you can:

    1. change pixelformat to 32bits for Depth buffer if supported by gfx
    2. lower Zfar/Znear ratio

      Znear is more significant (due to non linear Z values). The closer to the zero it is the worse this thing gets. If you are rendering objects with resolution in meters then there is no point to have z_near < 1.0

    3. stack up more frustrums

      In case you need to cover very high dynamic range you can render the scene in more then one pass. For more info see

      Especially the bullet #1

    4. Use linear Z-buffer

      This can be done with shaders getting rid of the non linearity.