• [OpenGL][SharpGL]用Polygon Offset解决z-fighting和stitching问题


    [OpenGL][SharpGL]用Polygon Offset解决z-fighting和stitching问题

    本文参考了(http://www.zeuscmd.com/tutorials/opengl/15-PolygonOffset.php),用SharpGL重写了示例代码,您可以点击文末的链接下载。

    什么是stitching和z-fighting

    在OpenGL中,如果想绘制一个多边形同时绘制其边界,可是先使用多边形模式GL_FILL绘制物体,然后使用多边形模式GL_LINE和不同的颜色再次绘制这个多边形。但是由于直线和多边形的光栅化方式不同,导致位于同一位置的多边形和直线的深度值并不相同,进而导致直线有时在多边形的里面,有时在多边形的外面,这种现象就是"Stiching"。

    而Z-fighting主要是指当两个面共面时,二者的深度值一样,深度缓冲就不能清楚的将它们两者分离开来,位于后面的图元上的一些像素就会被渲染到前面的图元上,最终导致图象在帧与帧之间产生微弱的闪光。

    (上面这几段是从(http://blog.csdn.net/oglmatrix/article/details/1793074)复制来的。)

     

    +BIT祝威+悄悄在此留下版了个权的信息说:

    Polygon Offset效果图

    解决这两个问题的方法就是使用Polygon Offset,当然你也可以使用模板测试,但Polygon Offset的速度会比模板缓存快。

    下面就是stitching的情况。

    下面就是z-fighting的情况。

    而理想的效果是这样的:

    下面是下文将介绍的Demo。

    这是没有使用Polygon Offset的情况。

    这是使用了Polygon Offset的情况。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    如何使用Polygon Offset

    下面用上图所示的模型来演示如何使用Polygon Offset。我们要画一个立方体,立方体的黑色线框,还有3个正方形

    Offset开关

    我们用一个bool变量记录是否启用Polygon Offset。这样就可以看清楚这项技术的效果了。

    1         /// <summary>
    2         /// determins whether enalbe or disable polygon offset.
    3         /// </summary>
    4         private bool offset = true;

    用立方体演示stitching

    我们画一个立方体和它的线条,用于演示stitching。

     1         void drawBox(OpenGL gl)
     2         {
     3             gl.Begin(OpenGL.GL_QUADS);
     4             // FRONT
     5             gl.Vertex(-0.5f, -0.5f, 0.5f);
     6             gl.Vertex(0.5f, -0.5f, 0.5f);
     7             gl.Vertex(0.5f, 0.5f, 0.5f);
     8             gl.Vertex(-0.5f, 0.5f, 0.5f);
     9             // BACK
    10             gl.Vertex(-0.5f, -0.5f, -0.5f);
    11             gl.Vertex(-0.5f, 0.5f, -0.5f);
    12             gl.Vertex(0.5f, 0.5f, -0.5f);
    13             gl.Vertex(0.5f, -0.5f, -0.5f);
    14             // LEFT
    15             gl.Vertex(-0.5f, -0.5f, 0.5f);
    16             gl.Vertex(-0.5f, 0.5f, 0.5f);
    17             gl.Vertex(-0.5f, 0.5f, -0.5f);
    18             gl.Vertex(-0.5f, -0.5f, -0.5f);
    19             // RIGHT
    20             gl.Vertex(0.5f, -0.5f, -0.5f);
    21             gl.Vertex(0.5f, 0.5f, -0.5f);
    22             gl.Vertex(0.5f, 0.5f, 0.5f);
    23             gl.Vertex(0.5f, -0.5f, 0.5f);
    24             // TOP
    25             gl.Vertex(-0.5f, 0.5f, 0.5f);
    26             gl.Vertex(0.5f, 0.5f, 0.5f);
    27             gl.Vertex(0.5f, 0.5f, -0.5f);
    28             gl.Vertex(-0.5f, 0.5f, -0.5f);
    29             // BOTTOM
    30             gl.Vertex(-0.5f, -0.5f, 0.5f);
    31             gl.Vertex(-0.5f, -0.5f, -0.5f);
    32             gl.Vertex(0.5f, -0.5f, -0.5f);
    33             gl.Vertex(0.5f, -0.5f, 0.5f);
    34             gl.End();
    35         }

    用正方形演示z-fighting

    我们在立方体的一个面上,画几个正方形,用于演示z-fighting。

    1         void drawPolygon(OpenGL gl)
    2         {
    3             gl.Begin(OpenGL.GL_QUADS);
    4             gl.Vertex(-0.5f, -0.5f, 0.0f);
    5             gl.Vertex(0.5f, -0.5f, 0.0f);
    6             gl.Vertex(0.5f, 0.5f, 0.0f);
    7             gl.Vertex(-0.5f, 0.5f, 0.0f);
    8             gl.End();
    9         }

    启用Polygon Offset

    Polygon Offset有多种使用方式。其中之一就是把立方体推远一点点,这样,线框和正方形就与立方体有了间隔,就不会产生干涉。(我的理解是,在执行深度测试前立方体的深度值被稍微增加了一点点,于是就跟线框区分开了。)

    启用Polygon Offset有三个可选参数(GL_POLYGON_OFFSET_POINT, GL_POLYGON_OFFSET_LINE 和GL_POLYGON_OFFSET_FILL),分别对应3种光栅化模式(GL_POINT, GL_LINE 和GL_FILL)。

    首先来渲染立方体,我们用GL_POLYGON_OFFSET_FILL来启用Polygon Offset。

    1             if (offset)
    2             {
    3                 gl.Enable(OpenGL.GL_POLYGON_OFFSET_FILL);

    指定偏移量

    启用了Polygon Offset,那么到底要把立方体推远多少呢?这需要用glPolygonOffset来指定。glPolygonOffset需要2个参数:GLfloat factor 和GLfloat units。

    每一个Fragment的深度值都会增加如下所示的偏移量:

    offset = (m * factor) + (r * units)

    m是多边形的深度的斜率(在光栅化阶段计算得出)中的最大值。这句话难以理解,你只需知道,一个多边形越是与近裁剪面(near clipping plan)平行,m就越接近0。

    r是能产生在窗口坐标系的深度值中可分辨的差异的最小值,r是由具体实现OpenGL的平台指定的一个常量。

    一个大于0的offset 会把模型推到离你(摄像机)更远一点的位置,相应地,一个小于0的offset 会把模型拉近。

    如果想要非常好地使用Polygon Offset,你需要做一些数学上的研究。不过一般而言,只需把1.0和0.0这样简单的值赋给glPolygonOffset即可满足需要。

    我们要把立方体推远一点,所以

    4                 gl.PolygonOffset(1.0f, 1.0f);
    5             }

    然后,我们绘制立方体,并关闭Polygon Offset。

    6             gl.Color(1.0f, 0.0f, 0.0f);
    7             gl.PolygonMode(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_FILL);
    8             drawBox(gl);
    9 
    10            if (offset)
    11                gl.Disable(OpenGL.GL_POLYGON_OFFSET_FILL);

    绘制正方形

    我们已经推远了立方体,所以原本在立方体平面上的绿色和黄色正方形就没有z-fighting现象了。直接绘制即可。

     1             gl.Color(0.0f, 1.0f, 0.0f);
     2             gl.PushMatrix();
     3             gl.Translate(-0.25f, -0.25f, 0.5f);
     4             gl.Scale(0.5f, 0.5f, 0.5f);
     5             drawPolygon(gl);
     6             gl.PopMatrix();
     7 
     8             gl.Color(1.0f, 1.0f, 0.0f);
     9             gl.PushMatrix();
    10             gl.Translate(0.25f, 0.25f, 0.5f);
    11             gl.Scale(0.5f, 0.5f, 0.5f);
    12             drawPolygon(gl);
    13             gl.PopMatrix();

    注意,绿色和黄色正方形两者之间没有重叠的部分,所以不会有z-fighting现象。

    但是蓝色正方形与绿色、黄色正方形都有重叠,所以会有z-fighting现象。为避免此现象,我们把-1.0赋予glPolygonOffset,即拉近蓝色正方形。

     1             if (offset)
     2             {
     3                 gl.Enable(OpenGL.GL_POLYGON_OFFSET_FILL);
     4                 gl.PolygonOffset(-1.0f, -1.0f);
     5             }
     6 
     7             gl.Color(0.0f, 0.0f, 1.0f);
     8             gl.PushMatrix();
     9             gl.Translate(0.0f, 0.0f, 0.5f);
    10             gl.Scale(0.5f, 0.5f, 0.5f);
    11             drawPolygon(gl);
    12             gl.PopMatrix();
    13 
    14             if (offset)
    15                 gl.Disable(OpenGL.GL_POLYGON_OFFSET_FILL);

    绘制立方体的线框

    如果直接绘制立方体的线框,线框会与立方体产生stitching现象。所以,如同拉近蓝色正方形一样,我们也把线框拉近一点。不同的是,我们把GL_POLYGON_OFFSET_LINE赋予glEnable。

     1             if (offset)
     2             {
     3                 gl.Enable(OpenGL.GL_POLYGON_OFFSET_LINE);
     4                 gl.PolygonOffset(-1.0f, -1.0f);
     5             }
     6 
     7             gl.Color(0.0f, 0.0f, 0.0f);
     8             gl.PolygonMode(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_LINE);
     9             drawBox(gl);
    10 
    11             if (offset)
    12                 gl.Disable(OpenGL.GL_POLYGON_OFFSET_LINE);

    启用禁用Polygon Offset

    我们用A键开控制是否启用Polygon Offset功能。

    1         void openGLControl_KeyDown(object sender, KeyEventArgs e)
    2         {
    3             if (e.KeyCode == Keys.A)
    4             {
    5                 offset = !offset;
    6             }
    7         }

    到此,Demo讲解完毕,您可以在上文查看启用禁用Polygon Offset的效果。

    +BIT祝威+悄悄在此留下版了个权的信息说:
    如果你发现及时使用了glPolygonOffset()也无效,那么可能是因为你只对面启用了glPolygonOffset,而没有对线条也启用glPolygonOffset。
  • 相关阅读:
    Git笔记
    排序学习LTR(1):排序算法的评价指标
    C++指针
    C++基础知识笔记
    Shell脚本--菜鸟教程笔记
    torch学习01-入门文档学习
    torch学习02-tensor学习
    torch学习0: 学习概览
    linux基础-用户创建及管理相关
    python-getattr() 函数 dir() 函数
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/polygon-offset-for-stitching-andz-fighting.html
Copyright © 2020-2023  润新知