Unity 几何着色器

shader Geometry Shader 几何着色器

Unity 几何着色器

如果学习不能带来价值,那将毫无意义

简介

    在顶点和片段着色器之间有一个可选的着色器,叫做几何着色器(Geometry Shader)。几何着色器以一个或多个表示为一个单独基本图形(primitive)即图元的顶点作为输入,比如可以是一个点或者三角形。几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。几何着色器有意思的地方在于它可以把(一个或多个)顶点转变为完全不同的基本图形(primitive),从而生成比原来多得多的顶点。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

输入

    几何着色器阶段可将整个图元作为输入,并能够在输出上生成顶点。
    必须首先指定单次调用几何着色器输出的顶点的最大数量(每个图元调用几何着色器)。这可以通过使用以下属性语法在着色器定义之前设置最大顶点数:
[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 阶段的输出。

声明

  1.  
  2. [maxvertexcount(3)] 
  3. void geometry_shader(point VS_OUTPUT IN[1], inout TriangleStream<GS_ 
  4. OUTPUT> triStream) { /*shader body*/ } 

示例

不做处理

    几何着色器位于顶点着色器和片元着色器之间,下面示例中几何着色器没做多余的效果,仅仅相当于默认的数据传递。

  1. Shader "ShaderCookbook/几何着色器/SimplePoint" 
  2. { 
  3. Properties 
  4. { 
  5. _MainTex ("Texture", 2D) = "white" {} 
  6. } 
  7. SubShader 
  8. { 
  9. Tags { "RenderType"="Opaque" } 
  10. LOD 100 
  11.  
  12. Pass 
  13. { 
  14. CGPROGRAM 
  15. #pragma vertex vert 
  16. //-------声明几何着色器 
  17. #pragma geometry geom 
  18. #pragma fragment frag 
  19.  
  20. #include "UnityCG.cginc" 
  21.  
  22. struct appdata 
  23. { 
  24. float4 vertex : POSITION; 
  25. float2 uv : TEXCOORD0; 
  26. }; 
  27.  
  28. //-------顶点向几何阶段传递数据 
  29. struct v2g{ 
  30. float4 vertex:SV_POSITION; 
  31. float2 uv:TEXCOORD0; 
  32. }; 
  33.  
  34. //-------几何阶段向片元阶段传递数据 
  35. struct g2f 
  36. { 
  37. float2 uv : TEXCOORD0; 
  38. float4 vertex : SV_POSITION; 
  39. }; 
  40.  
  41. sampler2D _MainTex; 
  42. float4 _MainTex_ST; 
  43.  
  44. v2g vert (appdata v) 
  45. { 
  46. v2g o; 
  47. o.vertex = UnityObjectToClipPos(v.vertex); 
  48. o.uv = TRANSFORM_TEX(v.uv, _MainTex); 
  49. return o; 
  50. } 
  51.  
  52. //-------静态制定单个调用的最大顶点个数 
  53. [maxvertexcount(3)] 
  54. void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream){ 
  55. for(int i=0;i<3;i++){ 
  56. g2f o=(g2f)0; 
  57. o.vertex=input[i].vertex; 
  58. o.uv=input[i].uv; 
  59.  
  60. //-----将一个顶点添加到输出流列表 
  61. outStream.Append(o); 
  62. } 
  63.  
  64. //-------restart strip可以模拟一个primitives list 
  65. outStream.RestartStrip(); 
  66. } 
  67.  
  68. fixed4 frag (g2f i) : SV_Target 
  69. { 
  70. // sample the texture 
  71. fixed4 col = tex2D(_MainTex, i.uv); 
  72. return col; 
  73. } 
  74. ENDCG 
  75. } 
  76. } 
  77. } 
  78.  

点

示例一:单纯的点

 

Unity 几何着色器 随笔 第1张
点

 

  1. Shader "ShaderCookbook/几何着色器/OnlyPoint" 
  2. { 
  3. Properties 
  4. { 
  5. _MainTex ("Texture", 2D) = "white" {} 
  6. } 
  7. SubShader 
  8. { 
  9. Tags { "RenderType"="Opaque" } 
  10. LOD 100 
  11.  
  12. Pass 
  13. { 
  14. CGPROGRAM 
  15. #pragma vertex vert 
  16. //-------声明几何着色器 
  17. #pragma geometry geom 
  18. #pragma fragment frag 
  19.  
  20. #include "UnityCG.cginc" 
  21.  
  22. struct appdata 
  23. { 
  24. float4 vertex : POSITION; 
  25. float2 uv : TEXCOORD0; 
  26. }; 
  27.  
  28. //-------顶点向几何阶段传递数据 
  29. struct v2g{ 
  30. float4 vertex:SV_POSITION; 
  31. float2 uv:TEXCOORD0; 
  32. }; 
  33.  
  34. //-------几何阶段向片元阶段传递数据 
  35. struct g2f 
  36. { 
  37. float2 uv : TEXCOORD0; 
  38. float4 vertex : SV_POSITION; 
  39. }; 
  40.  
  41. sampler2D _MainTex; 
  42. float4 _MainTex_ST; 
  43.  
  44. v2g vert (appdata v) 
  45. { 
  46. v2g o; 
  47. o.vertex = UnityObjectToClipPos(v.vertex); 
  48. o.uv = TRANSFORM_TEX(v.uv, _MainTex); 
  49. return o; 
  50. } 
  51.  
  52. //-------静态制定单个调用的最大顶点个数 
  53. [maxvertexcount(4)] 
  54. void geom(point v2g input[1],inout PointStream<g2f> outStream){ 
  55. g2f o=(g2f)0; 
  56. o.vertex=input[0].vertex; 
  57. o.uv=input[0].uv; 
  58.  
  59. outStream.Append(o); 
  60. } 
  61.  
  62. fixed4 frag (g2f i) : SV_Target 
  63. { 
  64. // sample the texture 
  65. fixed4 col = tex2D(_MainTex, i.uv); 
  66. return col; 
  67. } 
  68. ENDCG 
  69. } 
  70. } 
  71. } 
  72.  

示例二:散裂

    这里需要注意的是我们通过以v[0]为原点构建两个向量,通过这两个向量我们可以自定义点。

 

Unity 几何着色器 随笔 第2张
由s,t构成的仿射坐标系

 

 

Unity 几何着色器 随笔 第3张
炸裂

 

  1. Shader "ShaderCookbook/GeometryShader/分解" { 
  2. Properties { 
  3. _MainTex("Texture", 2 D) = "white" {} 
  4. _Height("Length", float) = 0.5 
  5. _Offset("Offset", float) = 0.1 
  6.  
  7. _StripColor("StripColor", Color) = (1, 1, 1, 1) 
  8. _OutColor("OutColor", Color) = (1, 1, 1, 1) 
  9. _InColor("InColor", Color) = (1, 1, 1, 1) 
  10. } 
  11. SubShader { 
  12. Cull off 
  13. Pass { 
  14. Tags { 
  15. "RenderType" = "Opaque" 
  16. } 
  17.  
  18. CGPROGRAM# pragma vertex vert# pragma fragment frag#include "UnityCG.cginc" 
  19.  
  20. struct appdata { 
  21. float4 vertex: POSITION; 
  22. float2 uv: TEXCOORD0; 
  23. }; 
  24.  
  25. struct v2f { 
  26. float4 vertex: SV_POSITION; 
  27. float4 objPos: TEXCOORD1; 
  28. float2 uv: TEXCOORD0; 
  29. }; 
  30.  
  31. sampler2D _MainTex; 
  32. float4 _MainTex_ST; 
  33. float _Height; 
  34. float _Offset; 
  35. fixed4 _StripColor; 
  36.  
  37. v2f vert(appdata v) { 
  38. v2f o; 
  39. o.vertex = UnityObjectToClipPos(v.vertex); 
  40. o.objPos = v.vertex; 
  41. o.uv = v.uv; 
  42. return o; 
  43. } 
  44.  
  45. fixed4 frag(v2f i): SV_Target { 
  46. fixed4 col = tex2D(_MainTex, i.uv); 
  47.  
  48. clip(_Height + _Offset - i.objPos.y); 
  49.  
  50. if (i.objPos.y > _Height) 
  51. col = _StripColor; 
  52.  
  53. return col; 
  54. } 
  55. ENDCG 
  56. } 
  57.  
  58. pass { 
  59. Tags { 
  60. "RenderType" = "Opaque" 
  61. } 
  62.  
  63. CGPROGRAM# pragma vertex vert# pragma geometry geome# pragma fragment frag#include "UnityCG.cginc" 
  64.  
  65. fixed4 _OutColor; 
  66. fixed4 _InColor; 
  67. float _Height; 
  68. float _Offset; 
  69.  
  70. struct appdata { 
  71. float4 vertex: POSITION; 
  72. float2 uv: TEXCOORD0; 
  73. float3 normal: NORMAL; 
  74. }; 
  75.  
  76. struct v2g { 
  77. float4 objPos: TEXCOORD0; 
  78. float3 normal: NORMAL; 
  79. }; 
  80.  
  81. struct g2f { 
  82. float4 vertex: SV_POSITION; 
  83. fixed4 col: TEXCOORD0; 
  84. }; 
  85.  
  86. void ADD_VERT(float4 v, g2f o, inout PointStream < g2f > outstream) { 
  87. o.vertex = v; 
  88. outstream.Append(o); 
  89. } 
  90.  
  91. v2g vert(appdata v) { 
  92. v2g o; 
  93. o.objPos = v.vertex; 
  94. o.normal = v.normal; 
  95. return o; 
  96. } 
  97.  
  98. [maxvertexcount(6)] 
  99. void geome(triangle v2g input[3], inout PointStream < g2f > outStream) { 
  100. g2f o; 
  101.  
  102. //--------将一个三角面三个顶点的平均位置作为均值 
  103. float4 vertex = (input[0].objPos + input[1].objPos + input[2].objPos) / 3.0; 
  104. float3 normal = (input[0].normal + input[1].normal + input[2].normal) / 3.0; 
  105.  
  106. if (vertex.y < _Height + _Offset) 
  107. return; 
  108.  
  109. //-------以v[0]为原点构建两个向量,用来在后续过程中通过这两个向量来构建三角面中自定义的点 
  110. float4 s = input[1].objPos - input[0].objPos; 
  111. float4 t = input[2].objPos - input[0].objPos; 
  112.  
  113. o.col = _OutColor * 2; 
  114. for (int i = 0; i < 3; i++) { 
  115. input[i].objPos.xyz += input[i].normal * (vertex.y - _Height); 
  116. input[i].objPos = UnityObjectToClipPos(input[i].objPos); 
  117. ADD_VERT(input[i].objPos, o, outStream); 
  118. } 
  119.  
  120. o.col = _InColor * 2; 
  121.  
  122. //-------通过s,t两个向量构建自定义点 
  123. float4 v[3]; 
  124.  
  125. v[0] = 0.2 * s + 0.2 * t; 
  126. v[1] = 0.4 * s + 0.6 * t; 
  127. v[2] = 0.6 * s + 0.4 * t; 
  128.  
  129. for (int i = 0; i < 3; i++) { 
  130. v[i].xyz += normal * (vertex.y - _Height); 
  131. v[i] = UnityObjectToClipPos(v[i]); 
  132. ADD_VERT(v[i], o, outStream); 
  133. } 
  134. } 
  135.  
  136. fixed4 frag(g2f i): SV_Target { 
  137. fixed4 col = i.col; 
  138. return col; 
  139. } 
  140. ENDCG 
  141.  
  142. } 
  143. } 
  144. } 

线

 

Unity 几何着色器 随笔 第4张
线

 

示例:wire frame

  1. Shader "ShaderCookbook/几何着色器/TriangleLine" 
  2. { 
  3. Properties 
  4. { 
  5. _MainTex ("Texture", 2D) = "white" {} 
  6. } 
  7. SubShader 
  8. { 
  9. Tags { "RenderType"="Opaque" } 
  10. LOD 100 
  11.  
  12. Pass 
  13. { 
  14. CGPROGRAM 
  15. #pragma vertex vert 
  16. //-------声明几何着色器 
  17. #pragma geometry geom 
  18. #pragma fragment frag 
  19.  
  20. #include "UnityCG.cginc" 
  21.  
  22. struct appdata 
  23. { 
  24. float4 vertex : POSITION; 
  25. float2 uv : TEXCOORD0; 
  26. }; 
  27.  
  28. //-------顶点向几何阶段传递数据 
  29. struct v2g{ 
  30. float4 vertex:SV_POSITION; 
  31. float2 uv:TEXCOORD0; 
  32. }; 
  33.  
  34. //-------几何阶段向片元阶段传递数据 
  35. struct g2f 
  36. { 
  37. float2 uv : TEXCOORD0; 
  38. float4 vertex : SV_POSITION; 
  39. }; 
  40.  
  41. sampler2D _MainTex; 
  42. float4 _MainTex_ST; 
  43.  
  44. v2g vert (appdata v) 
  45. { 
  46. v2g o; 
  47. o.vertex = UnityObjectToClipPos(v.vertex); 
  48. o.uv = TRANSFORM_TEX(v.uv, _MainTex); 
  49. return o; 
  50. } 
  51.  
  52. //-------静态制定单个调用的最大顶点个数 
  53. [maxvertexcount(3)] 
  54. //使用三角面作为输入,线段作为输出我们得到完整的线框 
  55. void geom(triangle v2g input[3],inout LineStream<g2f> outStream){ 
  56. g2f o=(g2f)0; 
  57. o.vertex=input[0].vertex; 
  58. o.uv=input[0].uv; 
  59.  
  60. outStream.Append(o); 
  61.  
  62.  
  63. o.vertex=input[1].vertex; 
  64. o.uv=input[1].uv; 
  65.  
  66. outStream.Append(o); 
  67.  
  68. o.vertex=input[2].vertex; 
  69. o.uv=input[2].uv; 
  70. outStream.Append(o); 
  71.  
  72. } 
  73.  
  74. fixed4 frag (g2f i) : SV_Target 
  75. { 
  76. // sample the texture 
  77. fixed4 col = tex2D(_MainTex, i.uv); 
  78. return col; 
  79. } 
  80. ENDCG 
  81. } 
  82. } 
  83. } 
  84.  

面

 

Unity 几何着色器 随笔 第5张
enter description here

 

示例:尖刺

  1. Shader "ShaderCookbook/GeometryShader/尖刺" { 
  2. Properties { 
  3. _MainTex("Texture", 2D) = "white" {} 
  4. _Length("Length", float) = 0.5 
  5. } 
  6. SubShader { 
  7. Tags { 
  8. "RenderType" = "Opaque" 
  9. } 
  10. LOD 100 
  11.  
  12. Pass { 
  13. CGPROGRAM 
  14. #pragma vertex vert 
  15. #pragma geometry geom 
  16. #pragma fragment frag 
  17. // make fog work 
  18. #pragma multi_compile_fog 
  19. #include "UnityCG.cginc" 
  20.  
  21. struct appdata { 
  22. float4 vertex: POSITION; 
  23. float2 uv: TEXCOORD0; 
  24. }; 
  25.  
  26. struct v2g { 
  27. float4 pos: SV_POSITION; 
  28. float2 uv: TEXCOORD0; 
  29. }; 
  30.  
  31. struct g2f { 
  32. float2 uv: TEXCOORD0; 
  33. float4 vertex: SV_POSITION; 
  34. }; 
  35.  
  36. sampler2D _MainTex; 
  37. float4 _MainTex_ST; 
  38. float _Length; 
  39.  
  40.  
  41. void ADD_VERT(float3 v, g2f o, inout TriangleStream < g2f > tristream) { 
  42. o.vertex = UnityObjectToClipPos(v); 
  43. tristream.Append(o); 
  44. } 
  45.  
  46.  
  47. void ADD_TRI(float3 p0, float3 p1, float3 p2, g2f o, inout TriangleStream < g2f > tristream) { 
  48. ADD_VERT(p0, o, tristream); 
  49. ADD_VERT(p1, o, tristream); 
  50. ADD_VERT(p2, o, tristream); 
  51. tristream.RestartStrip(); 
  52. } 
  53.  
  54.  
  55. v2g vert(appdata v) { 
  56. v2g o = (v2g) 0; 
  57. o.pos = v.vertex; 
  58. o.uv = TRANSFORM_TEX(v.uv, _MainTex); 
  59. return o; 
  60. } 
  61.  
  62. [maxvertexcount(9)] 
  63. void geom(triangle v2g input[3], inout TriangleStream < g2f > outStream) { 
  64. g2f o; 
  65.  
  66. //-----------这里通过三角面的两个边叉乘来获得垂直于三角面的法线方向 
  67. float3 s = (input[1].pos - input[0].pos).xyz; 
  68. float3 t = (input[2].pos - input[0].pos).xyz; 
  69.  
  70. float3 normal = normalize(cross(s, t)); 
  71.  
  72. float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz) / 3.0; 
  73. float2 centerUv = (input[0].uv + input[1].uv + input[2].uv) / 3.0; 
  74.  
  75. o.uv = centerUv; 
  76.  
  77. centerPos += normal * _Length * abs(sin(_Time.y * 5)); 
  78.  
  79. ADD_TRI(input[0].pos, centerPos, input[2].pos, o, outStream); 
  80. ADD_TRI(input[2].pos, centerPos, input[1].pos, o, outStream); 
  81. ADD_TRI(input[1].pos, centerPos, input[0].pos, o, outStream); 
  82.  
  83.  
  84. } 
  85. fixed4 frag(g2f i): SV_Target { 
  86. fixed4 col = tex2D(_MainTex, i.uv); 
  87. return col; 
  88. } 
  89. ENDCG 
  90. } 
  91. } 
  92. } 

Other

项目工程:链接: https://pan.baidu.com/s/1eGk6GHIfWzIFcAX6pxTGRA 提取码: fv75

eqm2Bj23236

参考一
参考二
参考三
可视化shader编程工具

这里推荐一款可视化shader编程工具,对美术同学非常友好,就像建模工具中的材质编辑器一样

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄