標籤:des cWeb style blog http java
【OpenGL4.0】GLSL-幾何著色器詳解和執行個體(GS:Geometry Shader)
一、什麼是幾何著色器(GS:Geometry Shader)
Input Assembler(IA)從頂點緩衝區上的輸入資料流中接收頂點資料,並且把資料項目轉換為規範的格式。vertex shader通常用來把頂點從模型空間變換到平面空間,vertex shader讀取一個頂點,輸出一個頂點。Pixel Shader讀取單一pixel屬性,輸出包含顏色和Z資訊的的片斷。而geometry shader是DirectX10提出的,把同一地區的所有頂點作為輸入,產生新的頂點或者地區。此外資料流輸出(steam output)把geometry shader輸出的頂點資訊複製為4個連續的輸出緩衝子集。理論上來說,steam output的輸出能力和Input Assembler的輸入能力相匹配。
Shader就是一段可以改變像素、頂點和幾何學特徵的小程式。Vertex Shader是專門處理多邊形頂點的。那麼Geometry shader就是專門用來處理情境中的幾何圖形。在過去Vertex Shader每一次運行只能處理一個頂點的資料,並且每次只能輸出一個頂點的結果。在整個遊戲情境中,繪製的幾何圖形的任務量非常龐大,如果僅僅依靠Vertex Shader單一來完成,效率會極其低下。
現在DX10的設計師們在頂點與像素的處理過程中又加入了(Geometry shader)幾何著色器。它可以根據頂點的資訊來批量處理幾何圖形,對Vertex附近的資料進行函數處理,快速創造出新的多邊形。通過stream out將這些結果傳遞給其他Shader或buffer,將CPU從複雜龐大的幾何運算中解放出來。大爆炸,粒子效果,瀑布流水等複雜又關聯的情境都可以用Geometry shader很逼真的表現出來。
圖一是渲染管線。
圖一 渲染管線
GS位於VS與PS之間,可以完成許多模型層面上的工作諸如LOD。以往這些工作都是在CPU上完成的,佔用了寶貴的CPU迴圈 —— CPU可是很繁忙的東西,遊戲邏輯、音樂、輸入接受都是靠它,卻無法提高多少效能,CPU的並行計算效能是遠遠無法和GPU相比的。
二、幾何著色器功能簡介
GS被設計針對每個圖元執行一次。它能夠訪問圖元的所有頂點,以及與其相關的輸入變數的值。換句話說,如果前一階段提供了某一個輸出變數,那麼GS就可以訪問圖元中所有頂點的那個變數的值。因此,GS中的輸入變數都是數群組類型。
需要注意的是,GS所接受的圖元和以前的不同,它只接受“可調整的”圖元。
可調整的圖元(Adjacency Primitive)
在OpenGL中,我們定義了新的圖元類型:
GL_LINES_ADJACENCY_EXT
GL_LINE_STRIP_ADJACENCY_EXT
GL_TRIANGLES_ADJACENCY_EXT
GL_TRIANGLE_STRIP_ADJECENCY_EXT
我們可以在glBegin()、glDrawElements()中將他們作為新的參數使用。下面分別說明這4中類型的特點。
(1)Lines with Adjacecy
4*N個頂點被提供,N是要繪製的線段的數目。線段在#1個#2之間繪製,#0和#3提供調整資訊。
(2)Line Strip with Adjacency
頂點數目是N+3,N是要繪製的線段的數目。線段在#1、#2、。。。、#N+1之間繪製,#0和#N+2提供調整資訊。
(3)Triangles with Adjacency
頂點數目是6*N,組成三角形的是#0、#2、#4.而#1、#3、#5定義了修正三角形。
(4)Triangle Strip with Adjacency
頂點數目是2N+4.#0、#2、。。。定義三角形序列。其他的定義調整三角形。
在著色器連結之前,必須調用glProgramParameter進行相應的設定。
(1)設定GS可以輸出的最大頂點數目
glProgramParameteriEXT( progname, GL_GEOMETRY_VERTICES_OUT_EXT, int value )
(2)設定GS接收的圖元的類型
glProgramParameteriEXT( progname, GL_GEOMETRY_INPUT_TYPE_EXT, int value )
需要注意的是,GS的輸出圖元的類型必須和輸入圖元的類型匹配。
value的可能取值是:
A、GL_POINTS
B、GL_LINES
對應的輸入圖元類型可以是:GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP
C、GL_LINES_ADJACENCY_EXT
對應的輸入圖元類型可以是:GL_LINES_ADJACENCY_EXT、GL_LINES_STRIP_ADJACENCY_EXT
D、GL_TRIANGLES
對應的輸入圖元類型可以是:GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
E、GL_TRIANGLE_ADJACENCY_EXT
對應的輸入圖元類型可以是:GL_TRIANGLES_ADJACENCY_EXT、GL_TRIANGLE_STRIP_ADJACENCY_EXT
(3)設定輸出圖元類型
glProgramParameteriEXT( progname, GL_GEOMETRY_OUTPUT_TYPE_EXT, int value )
value的可能取值是:
GL_POINTS
GL_LINE_STRIP
GL_TRIANGLE_STRIP
GS可以輸出0個、1個或者多個圖元。這些圖元的類型和它從前一階段接收到的圖元的類型不一定相同。然而,GS不能輸出多種類型的圖元。比如,GS可以接收三角形,輸出多個線段序列,或者是輸出0個或多個三角形序列。
這就是的GS有多種用途。下面是幾個典型例子。一個GS可以依據某些準則比如可見度來移除一些圖元。GS也可以產生額外的圖元擴大所渲染的對象的形狀。GS也可以計算圖元的額外資訊,而把圖元原封不動的輸出。GS也可以輸出和輸入圖元完全不同的圖元。
GLSL中GS與VS的互動
在GS中,綠色正方形所表示的是由變數gl_VerticesIn所指定的,即最大維數。
GS內建輸出變數:
varying out vec4 gl_FrontColor;
varying out vec4 gl_BackColor;
varying out vec4 gl_FrontSecondaryColor;
varying out vec4 gl_BackSecondaryColor;
varying out vec4 gl_TexCoord[]; // at most gl_MaxTextureCoords
varying out float gl_FogFragCoord;
GS內建輸入變數:
varying in vec4 gl_FrontColorIn[gl_VerticesIn];
varying in vec4 gl_BackColorIn[gl_VerticesIn];
varying in vec4 gl_FrontSecondaryColorIn[gl_VerticesIn];
varying in vec4 gl_BackSecondaryColorIn[gl_VerticesIn];
varying in vec4 gl_TexCoordIn[gl_VerticesIn][]; // at most will be// gl_MaxTextureCoords
varying in float gl_FogFragCoordIn[gl_VerticesIn];
varying in vec4 gl_PositionIn[gl_VerticesIn];
varying in float gl_PointSizeIn[gl_VerticesIn];
varying in vec4 gl_ClipVertexIn[gl_VerticesIn];
GS的功能實現基於兩個很重要的內建函數:EmitVertex和EndPrimitive.這兩個函數使得GS能夠傳送多個頂點或者圖元到管線的下一階段。GS為一個頂點定義輸出變數,然後調用EmitVertex。之後,GS就能夠接著定義下一個頂點的相關輸出變數,再次調用EmitVertex。在對圖元的所有頂點完成相同的操作之後,GS調用EndPrimitive讓OpenGL知道圖元的所有頂點都傳送完畢。如果沒有顯式調用EndPrimitive,系統會隱式調用。不過顯式調用是一個好的習慣,建議這麼做。如果GS沒有調用EmitVertex,那麼輸入的圖元就不會被渲染。
需要注意的是,GS保證輸出由輸入圖元產生的結果的順序和輸入圖元的順序是相同的。這會造成效能上的影響。比如,多個著色器單元並行運算,結果必須被儲存然後進行排序。為了在相容性和效率之間平衡,Shader Model4.0做了一個限制:每一次執行最多隻能產生1024個32位的值。
三、幾何著色器運用執行個體
下面介紹一個執行個體:用GS實現一個粒子系統,繪製了很多上面貼了紋理的小正方形。
效果如下:
頂點著色器:
#version 400layout (location = 0) in vec3 VertexPosition;uniform mat4 ModelViewMatrix;uniform mat3 NormalMatrix;uniform mat4 ProjectionMatrix;void main(){ gl_Position = ModelViewMatrix * vec4(VertexPosition,1.0);}
幾何著色器:
#version 400layout( points ) in;layout( triangle_strip, max_vertices = 4 ) out;uniform float Size2; // Half the width of the quaduniform mat4 ProjectionMatrix;out vec2 TexCoord;void main(){ mat4 m = ProjectionMatrix; gl_Position = m * (vec4(-Size2,-Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(0.0,0.0); EmitVertex(); gl_Position = m * (vec4(Size2,-Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(1.0,0.0); EmitVertex(); gl_Position = m * (vec4(-Size2,Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(0.0,1.0); EmitVertex(); gl_Position = m * (vec4(Size2,Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(1.0,1.0); EmitVertex(); EndPrimitive();}
片斷著色器:
#version 400in vec2 TexCoord;uniform sampler2D SpriteTex;layout( location = 0 ) out vec4 FragColor;void main(){ FragColor = texture(SpriteTex, TexCoord);}