来自 技术 2019-04-17 00:00 的文章

Unity 几何着色器 - 君莫笑-93

Unity 几何着色器shaderGeometry Shader几何着色器

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)triangleGL_TRIANGLES, GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3)triangleadj输入图元具有邻接三角形(6)

输出

几何着色器通过将顶点附加到输出流对象来一次输出一个顶点。 流的拓扑由固定声明确定,选择 TriangleStream、LineStream 和 PointStream 作为 GS 阶段的输出。

声明
  1. [maxvertexcount(3)]
  2. void geometry_shader(point VS_OUTPUT IN[1], inout TriangleStream<GS_
  3. 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. Pass
    12. {
    13. CGPROGRAM
    14. #pragma vertex vert
    15. //-------声明几何着色器
    16. #pragma geometry geom
    17. #pragma fragment frag
    18. #include "UnityCG.cginc"
    19. struct appdata
    20. {
    21. float4 vertex : POSITION;
    22. float2 uv : TEXCOORD0;
    23. };
    24. //-------顶点向几何阶段传递数据
    25. struct v2g{
    26. float4 vertex:SV_POSITION;
    27. float2 uv:TEXCOORD0;
    28. };
    29. //-------几何阶段向片元阶段传递数据
    30. struct g2f
    31. {
    32. float2 uv : TEXCOORD0;
    33. float4 vertex : SV_POSITION;
    34. };
    35. sampler2D _MainTex;
    36. float4 _MainTex_ST;
    37. v2g vert (appdata v)
    38. {
    39. v2g o;
    40. o.vertex = UnityObjectToClipPos(v.vertex);
    41. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    42. return o;
    43. }
    44. //-------静态制定单个调用的最大顶点个数
    45. [maxvertexcount(3)]
    46. void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream){
    47. for(int i=0;i<3;i++){
    48. g2f o=(g2f)0;
    49. o.vertex=input[i].vertex;
    50. o.uv=input[i].uv;
    51. //-----将一个顶点添加到输出流列表
    52. outStream.Append(o);
    53. }
    54. //-------restart strip可以模拟一个primitives list
    55. outStream.RestartStrip();
    56. }
    57. fixed4 frag (g2f i) : SV_Target
    58. {
    59. // sample the texture
    60. fixed4 col = tex2D(_MainTex, i.uv);
    61. return col;
    62. }
    63. ENDCG
    64. }
    65. }
    66. }
    67. 示例一:单纯的点
      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. Pass
      12. {
      13. CGPROGRAM
      14. #pragma vertex vert
      15. //-------声明几何着色器
      16. #pragma geometry geom
      17. #pragma fragment frag
      18. #include "UnityCG.cginc"
      19. struct appdata
      20. {
      21. float4 vertex : POSITION;
      22. float2 uv : TEXCOORD0;
      23. };
      24. //-------顶点向几何阶段传递数据
      25. struct v2g{
      26. float4 vertex:SV_POSITION;
      27. float2 uv:TEXCOORD0;
      28. };
      29. //-------几何阶段向片元阶段传递数据
      30. struct g2f
      31. {
      32. float2 uv : TEXCOORD0;
      33. float4 vertex : SV_POSITION;
      34. };
      35. sampler2D _MainTex;
      36. float4 _MainTex_ST;
      37. v2g vert (appdata v)
      38. {
      39. v2g o;
      40. o.vertex = UnityObjectToClipPos(v.vertex);
      41. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
      42. return o;
      43. }
      44. //-------静态制定单个调用的最大顶点个数
      45. [maxvertexcount(4)]
      46. void geom(point v2g input[1],inout PointStream<g2f> outStream){
      47. g2f o=(g2f)0;
      48. o.vertex=input[0].vertex;
      49. o.uv=input[0].uv;
      50. outStream.Append(o);
      51. }
      52. fixed4 frag (g2f i) : SV_Target
      53. {
      54. // sample the texture
      55. fixed4 col = tex2D(_MainTex, i.uv);
      56. return col;
      57. }
      58. ENDCG
      59. }
      60. }
      61. }
      62. 示例二:散裂

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

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

          线线

          示例: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. Pass
          12. {
          13. CGPROGRAM
          14. #pragma vertex vert
          15. //-------声明几何着色器
          16. #pragma geometry geom
          17. #pragma fragment frag
          18. #include "UnityCG.cginc"
          19. struct appdata
          20. {
          21. float4 vertex : POSITION;
          22. float2 uv : TEXCOORD0;
          23. };
          24. //-------顶点向几何阶段传递数据
          25. struct v2g{
          26. float4 vertex:SV_POSITION;
          27. float2 uv:TEXCOORD0;
          28. };
          29. //-------几何阶段向片元阶段传递数据
          30. struct g2f
          31. {
          32. float2 uv : TEXCOORD0;
          33. float4 vertex : SV_POSITION;
          34. };
          35. sampler2D _MainTex;
          36. float4 _MainTex_ST;
          37. v2g vert (appdata v)
          38. {
          39. v2g o;
          40. o.vertex = UnityObjectToClipPos(v.vertex);
          41. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
          42. return o;
          43. }
          44. //-------静态制定单个调用的最大顶点个数
          45. [maxvertexcount(3)]
          46. //使用三角面作为输入,线段作为输出我们得到完整的线框
          47. void geom(triangle v2g input[3],inout LineStream<g2f> outStream){
          48. g2f o=(g2f)0;
          49. o.vertex=input[0].vertex;
          50. o.uv=input[0].uv;
          51. outStream.Append(o);
          52. o.vertex=input[1].vertex;
          53. o.uv=input[1].uv;
          54. outStream.Append(o);
          55. o.vertex=input[2].vertex;
          56. o.uv=input[2].uv;
          57. outStream.Append(o);
          58. }
          59. fixed4 frag (g2f i) : SV_Target
          60. {
          61. // sample the texture
          62. fixed4 col = tex2D(_MainTex, i.uv);
          63. return col;
          64. }
          65. ENDCG
          66. }
          67. }
          68. }
          69. 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. Pass {
            12. CGPROGRAM
            13. #pragma vertex vert
            14. #pragma geometry geom
            15. #pragma fragment frag
            16. // make fog work
            17. #pragma multi_compile_fog
            18. #include "UnityCG.cginc"
            19. struct appdata {
            20. float4 vertex: POSITION;
            21. float2 uv: TEXCOORD0;
            22. };
            23. struct v2g {
            24. float4 pos: SV_POSITION;
            25. float2 uv: TEXCOORD0;
            26. };
            27. struct g2f {
            28. float2 uv: TEXCOORD0;
            29. float4 vertex: SV_POSITION;
            30. };
            31. sampler2D _MainTex;
            32. float4 _MainTex_ST;
            33. float _Length;
            34. void ADD_VERT(float3 v, g2f o, inout TriangleStream < g2f > tristream) {
            35. o.vertex = UnityObjectToClipPos(v);
            36. tristream.Append(o);
            37. }
            38. void ADD_TRI(float3 p0, float3 p1, float3 p2, g2f o, inout TriangleStream < g2f > tristream) {
            39. ADD_VERT(p0, o, tristream);
            40. ADD_VERT(p1, o, tristream);
            41. ADD_VERT(p2, o, tristream);
            42. tristream.RestartStrip();
            43. }
            44. v2g vert(appdata v) {
            45. v2g o = (v2g) 0;
            46. o.pos = v.vertex;
            47. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            48. return o;
            49. }
            50. [maxvertexcount(9)]
            51. void geom(triangle v2g input[3], inout TriangleStream < g2f > outStream) {
            52. g2f o;
            53. //-----------这里通过三角面的两个边叉乘来获得垂直于三角面的法线方向
            54. float3 s = (input[1].pos - input[0].pos).xyz;
            55. float3 t = (input[2].pos - input[0].pos).xyz;
            56. float3 normal = normalize(cross(s, t));
            57. float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz) / 3.0;
            58. float2 centerUv = (input[0].uv + input[1].uv + input[2].uv) / 3.0;
            59. o.uv = centerUv;
            60. centerPos += normal * _Length * abs(sin(_Time.y * 5));
            61. ADD_TRI(input[0].pos, centerPos, input[2].pos, o, outStream);
            62. ADD_TRI(input[2].pos, centerPos, input[1].pos, o, outStream);
            63. ADD_TRI(input[1].pos, centerPos, input[0].pos, o, outStream);
            64. }
            65. fixed4 frag(g2f i): SV_Target {
            66. fixed4 col = tex2D(_MainTex, i.uv);
            67. return col;
            68. }
            69. ENDCG
            70. }
            71. }
            72. }

              Other

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

              eqm2Bj23236

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

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