OpenGL overlapping ugly rendering
Asked Answered
T

3

5

I'm trying to render a scene with OpenGL 2.1 but the borders on overlapping shapes are weird. I tested some OpenGL initialisations but without any change. I reduce my issue to a simple test application with 2 sphere with the same result.

I tried several things about Gl_DEPTH_TEST, enable/disable smoothing without success.

Here is my result with 2 gluSphere :

enter image description here

We can see some sort of aliasing when a line will be enough to separate blue and red faces...

I use SharpGL but I think that it's not significant (as I use it only as a an OpenGL wrapper). Here my simplest code to render the same thing (You can copy it in a Form to test it) :

OpenGL gl;
IntPtr hdc;
int cpt;

private void Init()
{
    cpt = 0;
    hdc = this.Handle;

    gl = new OpenGL();
    gl.Create(SharpGL.Version.OpenGLVersion.OpenGL2_1, RenderContextType.NativeWindow, 500, 500, 32, hdc);

    gl.Enable(OpenGL.GL_DEPTH_TEST);
    gl.DepthFunc(OpenGL.GL_LEQUAL);

    gl.ClearColor(1.0F, 1.0F, 1.0F, 0);
    gl.ClearDepth(1);

    gl.MatrixMode(OpenGL.GL_PROJECTION);
    gl.Perspective(30, 1, 0.1F, 1.0E+7F);

    gl.MatrixMode(OpenGL.GL_MODELVIEW);
    gl.LookAt(0, 3000, 0, 0, 0, 0, 0, 0, 1);
}

private void Render(int angle)
{
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);

    RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red);
    RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue);

    gl.Blit(hdc);
}

private void RenderSphere(OpenGL gl, int x, int y, int z, int angle, int radius, Color col)
{
    IntPtr obj = gl.NewQuadric();

    gl.PushMatrix();
    gl.Translate(x, y, z);
    gl.Rotate(angle, 0, 0);

    gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
    gl.Sphere(obj, radius, 20, 10);

    gl.Color(new float[] { 0, 0, 0, 1 });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
    gl.Sphere(obj, radius, 20, 10);

    gl.DeleteQuadric(obj);

    gl.PopMatrix();
}

Thanks in advance for your advices !

EDIT :

I tested that without success :

gl.Enable(OpenGL.GL_LINE_SMOOTH);
gl.Enable(OpenGL.GL_POLYGON_SMOOTH);
gl.ShadeModel(OpenGL.GL_SMOOTH);

gl.Hint(OpenGL.GL_LINE_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_POLYGON_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST);

EDIT2 : With more faces, image with and without lines

enter image description here

It is ... different... but not pleasing.

Tanishatanitansy answered 9/5, 2019 at 9:15 Comment(11)
Did you tried the same with enabled smooth shading? You may read this post here: https://mcmap.net/q/2032711/-smooth-shading-in-openglMyxomycete
(Initial post edited withou tried flags.) I see in your link an advice about using lights. It is the case in my full application and the result is the same.Tanishatanitansy
@Rabbid76 Yes, but shouldn't shading smooth-out the ugly zick-zack overlapping pattern?Myxomycete
@Rabbid76 Then the anwer will be to increase the number of rendered faces till the border looks like a line, or?Myxomycete
@Rabbid76 Yes, but maybe it is not more "ugly" to the PO. He didn't stated exactly what is "not ugly".Myxomycete
This is a case of Z-Fighting. Refer to linkAstraddle
In the link of @PikanshuKumar, a sentence seems to be very important : z is stored non-linearly . The exact same scene with a way smaller factor (radius of 0.003 instead of 300) gives an expected result. I'll work on it in my main application (I'll apply a factor everywhere.....) and I came back for a feedback.Tanishatanitansy
@KiwiJaune Try lowering the ratio between near and far plane distance, e.g. gl.Perspective(30, 1, 0.1F, 1.0E+7F); -> gl.Perspective(30, 1, 1.0F, 1.0E+7F); (1:1e8 to 1:1e7)Cheeseparing
@Cheeseparing That's a really good advice !Tanishatanitansy
It's all about depth buffer (its precision, far and near plane) and Z-Fighting: learnopengl.com/Advanced-OpenGL/Depth-testingAvignon
From L.C link: "A second trick is to set the near plane as far as possible. In one of the previous sections we've discussed that precision is extremely large when close to the near plane so if we move the near plane farther from the viewer, we'll have significantly greater precision over the entire frustum range." - it's all about the ratio though. The closer the ration is to 1:1, the closer to linear z will get. Of course at some point you lose precision because of big numbers, and a far-away near plane will clip the scene and difficult to manageCheeseparing
T
3

Solution 1 (not a good one): Applying gl.Scale(0.0001, 0.0001, 0.0001); to the ModelView matrix

Solution 2 : The near plane has to be as far as possible to avoid compressing z value in a small range. In this case, use 10 instead of 0.1 is enough. The best is to compute an adapted value depending on objects distance (in this case the nearest object is at 2700)

I think we can focus on z is stored non-linearly in the @PikanshuKumar link and the implicit consequencies.

Result : Only the faces are cutted by a line: there is a straight line as separator at the equator. enter image description here

Those lines disappear as expected when we increase the number of faces.enter image description here

Tanishatanitansy answered 9/5, 2019 at 10:38 Comment(0)
C
3

The issue has 2 reasons.

The first one indeed is a Z-fighting issue, which is cause by the monstrous distance between the near and far plane

gl.Perspective(30, 1, 0.1F, 1.0E+7F);

and the fact that at perspective projection, the depth is not linear. See also How to render depth linearly ....

This can be improved by putting the near plane as close as possible to the geometry. Since the distance to the object is 3000.0 and the radius of the sphere is 300, the near plane has to be less than 2700.0:

e.g.

gl.Perspective(30, 1, 2690.0F, 5000.0F);

The second issue is caused by the fact, that the sphere consist of triangle primitives. As you suggested in your answer, you can improve that by increasing the number of primitives.

I will provide an alternative solution, by using a clip plane. Clip the red sphere at the bottom and the blue sphere at the top. Exactly in the plane where the spheres are intersecting, so that a cap is cut off from each sphere.
A clip plane can be set by glClipPlane and to be enabled by glEnable.
The parameters to the clipping plane are interpreted as a Plane Equation. The first 3 components of the plane equation are the normal vector to the clipping plane. The 4th component is the distance to the origin.

So the clip plane equation for the red sphere has to be {0, 0, -1, 50} and for the blue sphere {0, 0, 1, -50}.
Note, when glClipPlane is called, then the equation is transformed by the inverse of the modelview matrix. So the clip plane has to be set before the model transformations like rotation, translation and scale.

e.g.

private void Render(int angle)
{
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);

    double[] plane1 = new double[] {0, 0, -1, 50};
    RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red, plane1);

    double[] plane2 = new double[] {0, 0, 1, -50};
    RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue, plane2);

    gl.Blit(hdc);
}
private void RenderSphere(
    OpenGL gl, int x, int y, int z, int angle, int radius,
    Color col, double[] plane)
{
    IntPtr obj = gl.NewQuadric();

    gl.ClipPlane(OpenGL.GL_CLIP_PLANE0, plane);
    gl.Enable(OpenGL.GL_CLIP_PLANE0);

    gl.PushMatrix();
    gl.Translate(x, y, z);
    gl.Rotate(angle, 0, 0);

    gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
    gl.Sphere(obj, radius, 20, 10);

    gl.Color(new float[] { 0, 0, 0, 1 });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
    gl.Sphere(obj, radius, 20, 10);

    gl.DeleteQuadric(obj);

    gl.PopMatrix();

    gl.Disable(OpenGL.GL_CLIP_PLANE0);
}
Chaise answered 9/5, 2019 at 17:40 Comment(2)
Thanks for your answer. The first part is interesting (and sufficient), but the clipping is not a real solution since you are trying to fix specifically the issue between those 2 spheres whereas it's just a simple example to highlight the issue encountered in a far more complex application.Tanishatanitansy
@KiwiJaune Ok, I see.Chaise
S
2

You're killing depth buffer precision with the way you setup your projection matrix

gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.Perspective(30, 1, 0.1F, 1.0E+7F);

Essentially this compresses almost all of the depth buffer precision into the range 0.1 to 0.2 or so (I didn't do the math, just eyeballing it here).

In general you should choose the distance for the near clip plane to be as far away as possible, still keeping all the objects in your scene. The distance of the far plane doesn't matter that much (in fact, with the right matrix magic you can place it at infinity), but in general it's also a good idea to keep it as close as possible.

Suspensor answered 9/5, 2019 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.