[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)復制來的。)
Polygon Offset效果圖
解決這兩個問題的方法就是使用Polygon Offset,當然你也可以使用模板測試,但Polygon Offset的速度會比模板緩存快。
下面就是stitching的情況。
下面就是z-fighting的情況。
而理想的效果是這樣的:
下面是下文將介紹的Demo。
這是沒有使用Polygon Offset的情況。
這是使用了Polygon Offset的情況。
如何使用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的效果。
文章列表