Unity官方文檔關於Geometry Shader的內容較少。不過也是因為Unity的開發者大多數面向的是移動平台開發,所以Geometry Shader作為DirectX 10的特性並沒有被開發者廣泛使用。
首先要知道,Geometry Shader和Vertex Shader以及Fragment Shader的區別。
在DirectX 9的渲染管線中,可編程的Shader只有頂點着色器和片段着色器兩類。但是在DirectX 10開始,渲染管線增加了一個【可選】的幾何體着色器。這是與頂點着色器和片段着色器【理論可選】作為渲染管線必需的兩個着色器的主要區別。
幾何着色器在渲染管線中的位置是在頂點着色器和片段着色器之間,准確的說是和頂點着色器相鄰。所以在功能方面,幾何體着色器的操作和頂點着色器有相似性。一些原來位於頂點着色器中的計算,理論上完全可以轉移到幾何體着色器中進行。但實際操作中,並不建議那么做。因為幾何體着色器並行調用硬件困難,並行程度低,效率和頂點着色器有很大的差距,這是第二個區別。
頂點着色器是逐頂點操作,可以進行坐標變換等計算。
片段着色器是逐片段/像素操作,進行最終輸出顏色的計算。
幾何着色器同理,是逐圖元的操作。它的輸入是圖元,輸出也是圖元。
圖元是渲染對象在頂點着色器之后,光柵化之前的一種狀態。簡單的來說,就是包含點【點Point而不是頂點Vertex】或者線段或者三角形的集合。
經過這些簡單的了解,大概能猜到幾何着色器可以用來做什么了。比如細分【DirectX 11新增了更優的可選的細分着色器實現】,比如Low Poly,比如線框。Billboard也可以,不過Billboard可以在腳本和頂點Shader中同樣可以實現。
通過簡單的一個Billboard例子來講解幾何體着色器:
給一個Quad使用后的運行結果是產生Y軸約束,面向攝像機的Quad。

1 Shader "Custom/GS Billboard" 2 { 3 Properties 4 { 5 _SpriteTex ("Base (RGB)", 2D) = "white" {} 6 _Size ("Size", Range(0, 3)) = 0.5 7 } 8 9 SubShader 10 { 11 Pass 12 { 13 Tags { "RenderType"="Opaque" } 14 LOD 200 15 16 CGPROGRAM 17 #pragma target 5.0 18 #pragma vertex VS_Main 19 #pragma fragment FS_Main 20 #pragma geometry GS_Main 21 #include "UnityCG.cginc" 22 23 // ************************************************************** 24 // Data structures * 25 // ************************************************************** 26 struct GS_INPUT 27 { 28 float4 pos : POSITION; 29 float3 normal : NORMAL; 30 float2 tex0 : TEXCOORD0; 31 }; 32 33 struct FS_INPUT 34 { 35 float4 pos : POSITION; 36 float2 tex0 : TEXCOORD0; 37 }; 38 39 40 // ************************************************************** 41 // Vars * 42 // ************************************************************** 43 44 float _Size; 45 float4x4 _VP; 46 Texture2D _SpriteTex; 47 SamplerState sampler_SpriteTex; 48 49 // ************************************************************** 50 // Shader Programs * 51 // ************************************************************** 52 53 // Vertex Shader ------------------------------------------------ 54 GS_INPUT VS_Main(appdata_base v) 55 { 56 GS_INPUT output = (GS_INPUT)0; 57 58 output.pos = mul(_Object2World, v.vertex); 59 output.normal = v.normal; 60 output.tex0 = float2(0, 0); 61 62 return output; 63 } 64 65 66 67 // Geometry Shader ----------------------------------------------------- 68 [maxvertexcount(4)] 69 void GS_Main(point GS_INPUT p[1], inout TriangleStream<FS_INPUT> triStream) 70 { 71 float3 up = float3(0, 1, 0); 72 float3 look = _WorldSpaceCameraPos - p[0].pos; 73 look.y = 0; 74 look = normalize(look); 75 float3 right = cross(up, look); 76 77 float halfS = 0.5f * _Size; 78 79 float4 v[4]; 80 v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f); 81 v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f); 82 v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f); 83 v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f); 84 85 float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object); 86 FS_INPUT pIn; 87 pIn.pos = mul(vp, v[0]); 88 pIn.tex0 = float2(1.0f, 0.0f); 89 triStream.Append(pIn); 90 91 pIn.pos = mul(vp, v[1]); 92 pIn.tex0 = float2(1.0f, 1.0f); 93 triStream.Append(pIn); 94 95 pIn.pos = mul(vp, v[2]); 96 pIn.tex0 = float2(0.0f, 0.0f); 97 triStream.Append(pIn); 98 99 pIn.pos = mul(vp, v[3]); 100 pIn.tex0 = float2(0.0f, 1.0f); 101 triStream.Append(pIn); 102 } 103 104 105 106 // Fragment Shader ----------------------------------------------- 107 float4 FS_Main(FS_INPUT input) : COLOR 108 { 109 return _SpriteTex.Sample(sampler_SpriteTex, input.tex0); 110 } 111 112 ENDCG 113 } 114 } 115 }
設定着色器編譯目標級別為5.0。不過根據Unity的文檔,4.0的着色器編譯目標級別就已經支持Geometry Shader了。
#pragma target 5.0
設定幾何體着色器函數名稱為GS_Main【可自定義】。
#pragma geometry GS_Main
設置頂點着色器向幾何體着色器輸出的最大頂點數量為4。
[maxvertexcount(4)]
幾何體着色器輸入的圖元是點,數量為1。輸出的圖元是三角形流。
void GS_Main(point GS_INPUT p[1], inout TriangleStream<FS_INPUT> triStream)
Append()是幾何着色器內置的向輸出流附加頂點的函數,因為允許的最大輸出頂點數量就是4,所以append四次,輸出四個頂點。
triStream.Append(pIn);
其他沒有什么新東西要講,簡單的描述一下這個Shader的流程。
頂點着色器:
將頂點變換到世界空間,同時傳遞頂點法線和填充紋理坐標。
幾何着色器:
1.定義一個垂直向上的向量up。
2.計算觀察向量look。
3.將look向量的Y軸設為0並規格化,使得look向量成為平行於XZ平面指向攝像機的單位向量。
4.計算up向量和look向量的叉乘,得到一個垂直於二者的新向量right。
5.計算Size的一半halfS。
6.將輸入的點,以該點為中心點,分別向right向量正負軸向,up向量正負軸向移動halfS的距離,四個頂點的四個頂點的坐標。
7.將頂點依次轉換到投影空間,分配UV並Append到輸出流上,最后輸出。
片段着色器:
根據UV對貼圖進行采樣,返回顏色。