文章出處

[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。

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()