OpenGL重叠的丑陋渲染

时间:2019-05-09 09:15:15

标签: opengl opengl-compat

我正在尝试使用OpenGL 2.1渲染场景,但是重叠形状上的边框很奇怪。我测试了一些OpenGL初始化,但是没有任何变化。我将问题简化为一个具有2个球体的简单测试应用程序,结果相同。

我尝试了有关Gl_DEPTH_TEST的几件事,启用/禁用平滑操作没有成功。

这是我用2 gluSphere的结果:

enter image description here

当一行足以分隔蓝色和红色面时,我们可以看到某种混叠...

我使用SharpGL,但我认为它并不重要(因为我仅将其用作OpenGL包装器)。这是我渲染最简单的代码(可以将其复制到Form中进行测试):

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();
}

提前感谢您的建议!

编辑:

我测试失败了:

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:具有更多的面孔,带有和不带有线条的图像

enter image description here

这是……与众不同……但并不令人愉悦。

3 个答案:

答案 0 :(得分:3)

解决方案1(不好):将gl.Scale(0.0001, 0.0001, 0.0001);应用于ModelView矩阵

解决方案2:必须尽可能靠近近平面,以免在较小范围内压缩z值。在这种情况下,使用10代替0.1就足够了。最好是根据物体的距离(在这种情况下,最近的物体为2700)计算一个适应值

我认为我们可以专注于@PikanshuKumar链接中的z is stored non-linearly和隐含的后果。

结果: 只有面被线切割:赤道上有一条直线作为分隔符。 enter image description here

当我们增加面孔数量时,这些线条会按预期消失。enter image description here

答案 1 :(得分:2)

问题有两个原因。

第一个确实是Z-fighting问题,这是由于近平面和远平面之间的距离太远所致

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

以及在透视投影时深度不是线性的事实。另请参见How to render depth linearly ...

可以通过将近平面尽可能靠近几何图形来改善此效果。由于到物体的距离是3000.0,球体的半径是300,所以近平面必须小于2700.0:

例如

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

第二个问题是由于球体由三角形图元组成。正如您在答案中所建议的那样,您可以通过增加基本体的数量来改善这一点。

我将通过使用剪切平面提供替代解决方案。在底部剪裁红色球体,在顶部剪裁蓝色球体。恰好在球体相交的平面中,因此从每个球体上都剪下了盖帽。
剪切平面可以由glClipPlane设置,并可以由glEnable启用。
剪切平面的参数被解释为Plane Equation。 平面方程的前三个分量是剪切平面的法线向量。第四个分量是到原点的距离。

因此,红色球体的剪切平​​面方程必须为{0, 0, -1, 50},蓝色球体的剪切平​​面方程必须为{0, 0, 1, -50}
注意,当调用glClipPlane时,方程将通过模型视图矩阵的逆矩阵进行变换。因此必须在旋转,平移和缩放等模型转换之前设置裁剪平面。

例如

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);
}

答案 2 :(得分:1)

您通过设置投影矩阵的方式来破坏深度缓冲区的精度

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

基本上,这会将几乎所有深度缓冲区精度压缩到0.1到0.2左右的范围内(我没有做数学运算,只是在这里进行了研究)。

通常,您应该选择使近裁剪平面的距离尽可能远,同时仍将所有对象保留在场景中。远平面的距离无关紧要(实际上,借助正确的矩阵魔术,您可以将其置于无穷远),但是总的来说,使其尽可能靠近也是个好主意。