3

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;

}
FunnyFunkyBuggy
  • 495
  • 1
  • 7
  • 21
  • 3
    Looks like z-fighting to me. What projection matrix do you use? – BDL Feb 11 '17 at 11:06
  • 1
    yeap I agree the important stuff is how much bits you have on Z-Buffer and what is `Z_near,Z_far` values and your avg grid size (triangle). Try to lower `Z_far/Z_near` for example increase Z_near few times that should help – Spektre Feb 11 '17 at 11:32
  • I added my code for the projection matrix. I remembered that it was referenced from the internet. – FunnyFunkyBuggy Feb 11 '17 at 12:36
  • @BDL If that was due to z-fighting appear, should the image be flickering? In my case the "coastline" is not flickering. It looks like a floating zig-zag. – FunnyFunkyBuggy Feb 11 '17 at 12:50
  • @FunnyFunkyBuggy: Z-fighting does not induce flickering, but will certainly create these artifacts. You have a far/near factor of 10^4. That will most certainly create Z-fighting. – datenwolf Feb 11 '17 at 14:05
  • So, what is the rule of thumb when deciding the far/near ratio? – FunnyFunkyBuggy Feb 11 '17 at 15:03
  • 1
    Put yourself at the camera. How far is the nearest object? Set the near plane about there. – Ripi2 Feb 11 '17 at 15:43

2 Answers2

4

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.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
2

You are getting z-fighting. This means that the z difference between two overlapping triangles is very close, and computers only have so much precision. So you're losing sufficient depth precision to render your scene correctly.

The reason for that is your projection near-z being extremely close. Moving it to 1.0f would be helpful. Of course, if you move your far-z out to 10,000, you'll have a similar problem (though not quite as bad).

If you have access to OpenGL 4.5 or ARB_clip_control, you can vastly improve the situation without changing your projection near/far values. But this requires doing three things:

  1. Rendering to a floating point depth buffer. That will generally require rendering to an FBO (I don't know of a way to create a default framebuffer with a floating-point depth format). The depth attachment format would be GL_DEPTH_COMPONENT32F or similar.

  2. Reverse your near/far values. Since floating-point values have more precision towards zero, and the depth function already biases precision towards the near values, by reversing your near/far range, you apply the floating-point precision to the far values rather than the already-precise near ones.

  3. Set your clip space to use a [0, 1] Z range:

    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
    

    If you want the full details of what this does, along with all of the mathematics go here. To summarize, you're changing OpenGL's NDC-space Z range from [-1, 1] to [0, 1]. This affects both clipping and the window coordinate transform. By doing this, you avoid having the window coordinate transform perform a + 0.5f on the value, which would otherwise destroy your floating-point exponent. This makes the previous step actually helpful; without this, the previous step would accomplish nothing.

    Of course, you also need to change how you build your perspective projection matrix, since it was designed to go to a [-1, 1] range:

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

    Also, remember that you need to reverse the near and far z values you pass into this function.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982