Modern OpenGL用Shader拾取VBO內單一圖元的思路和實現(2)
上一篇里介紹了Color-Coded Picking的思路和最基本的實現。在處理GL_POINTS時已經沒有問題,但是處理GL_LINES、GL_TRIANGLES等時會遇到同一圖元的各個頂點顏色不同的問題,這就不能正確拾取了,本篇來解決這個問題。
對于GL_LINES,可以用 int objectID = gl_VertexID / 2; 來使得每個線段圖元的兩個頂點顏色分別相同;對于GL_TRIANGLES,則用 int objectID = gl_VertexID / 3; 。但是這個方法無法應用到存在共享頂點的GL_LINE_STRIP、GL_TRIANGLE_STRIP、GL_LINE_LOOP等情況。所以要另辟蹊徑。
ShadeMode
首先介紹一下glShadeMode()這個函數。就是靠它才解決了本文的問題。
glShadeMode()
在(https://www.opengl.org/sdk/docs/man2/xhtml/glShadeModel.xml)有它的專業解釋,里面有這樣一張表格。
Primitive Type of Polygon i |
Vertex |
Single polygon ( i == 1 ) |
1 |
Triangle strip |
i + 2 |
Triangle fan |
i + 2 |
Independent triangle |
3 × i |
Quad strip |
2 × i + 2 |
Independent quad |
4 × i |
這個表格的意思是:在 glShadeMode(GL_FLAT); 狀態下,在繪制某種圖元時,其顏色由第i個頂點的顏色決定。(其實就是由定義一個圖元的最后一個頂點的顏色決定)
單看表格難以理解,用下面的Demo進行說明。
這個Demo繪制了8個頂點,其位置和顏色如下(同上圖左側所示)

1 gl.Color(0, 0, 0); 2 gl.Vertex(0, 0); 3 4 gl.Color(1f, 0, 0); 5 gl.Vertex(0, 1); 6 7 gl.Color(0, 1f, 0); 8 gl.Vertex(1, 0); 9 10 gl.Color(1f, 1f, 0); 11 gl.Vertex(1, 1); 12 13 gl.Color(0, 0, 1f); 14 gl.Vertex(2, 0); 15 16 gl.Color(1f, 0, 1f); 17 gl.Vertex(2, 1); 18 19 gl.Color(0, 1f, 1f); 20 gl.Vertex(3, 0); 21 22 gl.Color(1f, 1f, 1f); 23 gl.Vertex(3, 1);
下面分別看一下在 glShadeMode(GL_SMOOTH); 和 glShadeMode(GL_FLAT); 模式下,用這個8個頂點繪制各種圖元的情況。
這是GL_SMOOTH 模式下的GL_LINE_STRIP。
這是GL_FLAT模式下的GL_LINE_STRIP。
這是GL_SMOOTH 模式下的GL_TRIANGLES。
這是GL_FLAT 模式下的GL_TRIANGLES。
這是GL_SMOOTH 模式下的GL_TRIANGLE_STRIP。
這是GL_FLAT模式下的GL_TRIANGLE_STRIP。
這是GL_SMOOTH 模式下的GL_QUAD_STRIP。
這是GL_FLAT 模式下的GL_QUAD_STRIP。
您可以點此下載此Demo慢慢對比。
GL_FLAT的作用
通過上面一節的觀察可以看到,GL_FLAT模式下,各種類型的圖元的顏色都是由繪制它的最后一個頂點的顏色給出的。
這么絕妙的顏色分配方案簡直就是為了解決本文的問題而設計的。
我們只需在做Picking的繪制時,在GL_FLAT狀態下繪制圖元,就可以用glReadPixel()獲取到應拾取的圖元的最后一個頂點的編號。根據上一節的表格,很容易推算出此編號代表的圖元。
'flat' in GLSL
不過這又帶來一個小問題:glShadeMode();在使用Shader+VBO時是無效的。不過Modern OpenGL必然要有能代替它的功能:GLSL里的flat關鍵字。給in/out變量附加一個flat,就相當于Legacy OpenGL里調用了glShadeMode(GL_FLAT);
flat out vec4 pass_Color; // glShadeMode(GL_FLAT); in legacy opengl.
flat in vec4 pass_Color; // glShadeMode(GL_FLAT); in legacy opengl.
這也是本文與上一篇的程序中唯一的區別。
未完待續
拾取一個VBO里的單個圖元的問題已經徹底解決了。那么來看下一個問題:一個場景里可能會有多個VBO,此時每個VBO的gl_VertexID都是從0開始的,那么如何區分不同VBO里的圖元呢?我們下回分解。
文章列表