Unity 幾何着色器
Unity 幾何着色器
如果學習不能帶來價值,那將毫無意義
簡介
在頂點和片段着色器之間有一個可選的着色器,叫做幾何着色器(Geometry Shader)。幾何着色器以一個或多個表示為一個單獨基本圖形(primitive)即圖元的頂點作為輸入,比如可以是一個點或者三角形。幾何着色器在將這些頂點發送到下一個着色階段之前,可以將這些頂點轉變為它認為合適的內容。幾何着色器有意思的地方在於它可以把(一個或多個)頂點轉變為完全不同的基本圖形(primitive),從而生成比原來多得多的頂點。
輸入
幾何着色器階段可將整個圖元作為輸入,並能夠在輸出上生成頂點。
必須首先指定單次調用幾何着色器輸出的頂點的最大數量(每個圖元調用幾何着色器)。這可以通過使用以下屬性語法在着色器定義之前設置最大頂點數:
[maxvertexcount(N)]
其中N是幾何着色器為單個調用輸出的頂點的最大數量。幾何着色器可以在每次調用時輸出的頂點數量是可變的,但不能超過定義的最大值。出於性能考慮,最大頂點數應盡可能小; [NVIDIA08]指出,當GS輸出在1到20個標量之間時,可以實現GS的性能峰值,如果GS輸出在27-40個標量之間,則性能下降50%。每次調用的標量輸出數是最大頂點輸出數和輸出頂點類型結構中的標量數的乘積。
基本圖形 | 描述 |
---|---|
point | 繪制GL_POINTS基本圖形的時候(1) |
line | 當繪制GL_LINES或GL_LINE_STRIP(2)時 |
lineadj | 輸入圖元具有鄰接線段(4) |
triangle | GL_TRIANGLES, GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3) |
triangleadj | 輸入圖元具有鄰接三角形(6) |
輸出
幾何着色器通過將頂點附加到輸出流對象來一次輸出一個頂點。 流的拓撲由固定聲明確定,選擇 TriangleStream、LineStream 和 PointStream 作為 GS 階段的輸出。
聲明
- [maxvertexcount(3)]
- void geometry_shader(point VS_OUTPUT IN[1], inout TriangleStream<GS_
- OUTPUT> triStream) { /*shader body*/ }
示例
不做處理
幾何着色器位於頂點着色器和片元着色器之間,下面示例中幾何着色器沒做多余的效果,僅僅相當於默認的數據傳遞。
- Shader "ShaderCookbook/幾何着色器/SimplePoint"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- //-------聲明幾何着色器
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------頂點向幾何階段傳遞數據
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------幾何階段向片元階段傳遞數據
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------靜態制定單個調用的最大頂點個數
- [maxvertexcount(3)]
- void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream){
- for(int i=0;i<3;i++){
- g2f o=(g2f)0;
- o.vertex=input[i].vertex;
- o.uv=input[i].uv;
- //-----將一個頂點添加到輸出流列表
- outStream.Append(o);
- }
- //-------restart strip可以模擬一個primitives list
- outStream.RestartStrip();
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
點
示例一:單純的點

- Shader "ShaderCookbook/幾何着色器/OnlyPoint"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- //-------聲明幾何着色器
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------頂點向幾何階段傳遞數據
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------幾何階段向片元階段傳遞數據
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------靜態制定單個調用的最大頂點個數
- [maxvertexcount(4)]
- void geom(point v2g input[1],inout PointStream<g2f> outStream){
- g2f o=(g2f)0;
- o.vertex=input[0].vertex;
- o.uv=input[0].uv;
- outStream.Append(o);
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
示例二:散裂
這里需要注意的是我們通過以v[0]為原點構建兩個向量,通過這兩個向量我們可以自定義點。


- Shader "ShaderCookbook/GeometryShader/分解" {
- Properties {
- _MainTex("Texture", 2 D) = "white" {}
- _Height("Length", float) = 0.5
- _Offset("Offset", float) = 0.1
- _StripColor("StripColor", Color) = (1, 1, 1, 1)
- _OutColor("OutColor", Color) = (1, 1, 1, 1)
- _InColor("InColor", Color) = (1, 1, 1, 1)
- }
- SubShader {
- Cull off
- Pass {
- Tags {
- "RenderType" = "Opaque"
- }
- CGPROGRAM
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- };
- struct v2f {
- float4 vertex: SV_POSITION;
- float4 objPos: TEXCOORD1;
- float2 uv: TEXCOORD0;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float _Height;
- float _Offset;
- fixed4 _StripColor;
- v2f vert(appdata v) {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.objPos = v.vertex;
- o.uv = v.uv;
- return o;
- }
- fixed4 frag(v2f i): SV_Target {
- fixed4 col = tex2D(_MainTex, i.uv);
- clip(_Height + _Offset - i.objPos.y);
- if (i.objPos.y > _Height)
- col = _StripColor;
- return col;
- }
- ENDCG
- }
- pass {
- Tags {
- "RenderType" = "Opaque"
- }
- CGPROGRAM
- fixed4 _OutColor;
- fixed4 _InColor;
- float _Height;
- float _Offset;
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- float3 normal: NORMAL;
- };
- struct v2g {
- float4 objPos: TEXCOORD0;
- float3 normal: NORMAL;
- };
- struct g2f {
- float4 vertex: SV_POSITION;
- fixed4 col: TEXCOORD0;
- };
- void ADD_VERT(float4 v, g2f o, inout PointStream < g2f > outstream) {
- o.vertex = v;
- outstream.Append(o);
- }
- v2g vert(appdata v) {
- v2g o;
- o.objPos = v.vertex;
- o.normal = v.normal;
- return o;
- }
- [maxvertexcount(6)]
- void geome(triangle v2g input[3], inout PointStream < g2f > outStream) {
- g2f o;
- //--------將一個三角面三個頂點的平均位置作為均值
- float4 vertex = (input[0].objPos + input[1].objPos + input[2].objPos) / 3.0;
- float3 normal = (input[0].normal + input[1].normal + input[2].normal) / 3.0;
- if (vertex.y < _Height + _Offset)
- return;
- //-------以v[0]為原點構建兩個向量,用來在后續過程中通過這兩個向量來構建三角面中自定義的點
- float4 s = input[1].objPos - input[0].objPos;
- float4 t = input[2].objPos - input[0].objPos;
- o.col = _OutColor * 2;
- for (int i = 0; i < 3; i++) {
- input[i].objPos.xyz += input[i].normal * (vertex.y - _Height);
- input[i].objPos = UnityObjectToClipPos(input[i].objPos);
- ADD_VERT(input[i].objPos, o, outStream);
- }
- o.col = _InColor * 2;
- //-------通過s,t兩個向量構建自定義點
- float4 v[3];
- v[0] = 0.2 * s + 0.2 * t;
- v[1] = 0.4 * s + 0.6 * t;
- v[2] = 0.6 * s + 0.4 * t;
- for (int i = 0; i < 3; i++) {
- v[i].xyz += normal * (vertex.y - _Height);
- v[i] = UnityObjectToClipPos(v[i]);
- ADD_VERT(v[i], o, outStream);
- }
- }
- fixed4 frag(g2f i): SV_Target {
- fixed4 col = i.col;
- return col;
- }
- ENDCG
- }
- }
- }
線

示例:wire frame
- Shader "ShaderCookbook/幾何着色器/TriangleLine"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- //-------聲明幾何着色器
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------頂點向幾何階段傳遞數據
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------幾何階段向片元階段傳遞數據
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------靜態制定單個調用的最大頂點個數
- [maxvertexcount(3)]
- //使用三角面作為輸入,線段作為輸出我們得到完整的線框
- void geom(triangle v2g input[3],inout LineStream<g2f> outStream){
- g2f o=(g2f)0;
- o.vertex=input[0].vertex;
- o.uv=input[0].uv;
- outStream.Append(o);
- o.vertex=input[1].vertex;
- o.uv=input[1].uv;
- outStream.Append(o);
- o.vertex=input[2].vertex;
- o.uv=input[2].uv;
- outStream.Append(o);
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
面

示例:尖刺
- Shader "ShaderCookbook/GeometryShader/尖刺" {
- Properties {
- _MainTex("Texture", 2D) = "white" {}
- _Length("Length", float) = 0.5
- }
- SubShader {
- Tags {
- "RenderType" = "Opaque"
- }
- LOD 100
- Pass {
- CGPROGRAM
- // make fog work
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- };
- struct v2g {
- float4 pos: SV_POSITION;
- float2 uv: TEXCOORD0;
- };
- struct g2f {
- float2 uv: TEXCOORD0;
- float4 vertex: SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float _Length;
- void ADD_VERT(float3 v, g2f o, inout TriangleStream < g2f > tristream) {
- o.vertex = UnityObjectToClipPos(v);
- tristream.Append(o);
- }
- void ADD_TRI(float3 p0, float3 p1, float3 p2, g2f o, inout TriangleStream < g2f > tristream) {
- ADD_VERT(p0, o, tristream);
- ADD_VERT(p1, o, tristream);
- ADD_VERT(p2, o, tristream);
- tristream.RestartStrip();
- }
- v2g vert(appdata v) {
- v2g o = (v2g) 0;
- o.pos = v.vertex;
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- [maxvertexcount(9)]
- void geom(triangle v2g input[3], inout TriangleStream < g2f > outStream) {
- g2f o;
- //-----------這里通過三角面的兩個邊叉乘來獲得垂直於三角面的法線方向
- float3 s = (input[1].pos - input[0].pos).xyz;
- float3 t = (input[2].pos - input[0].pos).xyz;
- float3 normal = normalize(cross(s, t));
- float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz) / 3.0;
- float2 centerUv = (input[0].uv + input[1].uv + input[2].uv) / 3.0;
- o.uv = centerUv;
- centerPos += normal * _Length * abs(sin(_Time.y * 5));
- ADD_TRI(input[0].pos, centerPos, input[2].pos, o, outStream);
- ADD_TRI(input[2].pos, centerPos, input[1].pos, o, outStream);
- ADD_TRI(input[1].pos, centerPos, input[0].pos, o, outStream);
- }
- fixed4 frag(g2f i): SV_Target {
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
Other
項目工程:鏈接: https://pan.baidu.com/s/1eGk6GHIfWzIFcAX6pxTGRA 提取碼: fv75
解壓密碼:https://fgk.pw/i/eqm2Bj23236
這里推薦一款可視化shader編程工具,對美術同學非常友好,就像建模工具中的材質編輯器一樣