Unity3D Shader系列之几何着色器基础

Unity3D Shader系列之几何着色器基础

2020年12月19日 1 作者 老王

1.什么是几何着色器

最近一直在练习写Shader,发现用几何着色器可以写出特别酷炫的效果。于是去学习了一下,这里做个总结以备查阅。
几何着色器位于渲染流水线的几何阶段,在顶点着色器与片元着色器之间。
GPU流水线
备注:图片来源于冯乐乐的《Unity Shader入门精要》p9
其在Shader中是可选的,完全可编程的。
顶点着色器是以顶点数据(局部坐标、法线方向、纹理坐标等)做为输入,然后输出齐次裁剪空间下的顶点坐标、顶点颜色以及其他阶段需要的信息。每个顶点都会调用一次顶点着色器。顶点着色器不能够创建和销毁任何顶点,也不知道此顶点与其他顶点的关系(比如这个顶点与其他顶点是否在同一个三角形里面)。
而几何着色器以完整的图元(Primitive)作为输入数据,输出经过我们处理后的图元。我们可以在几何着色器里面去创建或销毁顶点,完全控制输出的图元个数与类型。几何着色器的输入图元和输出图元都可以为点、线、面任一种。

2 语法

几何着色器的语法还是挺简单的,这里以一个Demo为例。
效果

Shader "Custom/Geometry/Wireframe3"
{
    Properties
    {
        _WireFrameColor ("Wireframe Color", color) = (1.0, 1.0, 1.0, 1.0)
    }

    SubShader
    {
        Pass
        {
            Tags { "RenderType" = "Opaque" "RenderQueue" = "Geometry"}

            CGPROGRAM

            // 1.设置着色器编译目标等级
            #pragma target 4.0

            #pragma vertex vert
            // 2.声明几何着色器方法
            #pragma geometry geo
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2g {
                float4 pos : SV_POSITION;
            };

            struct g2f {
                float4 pos : SV_POSITION;
            };

            fixed4 _WireFrameColor;

            v2g vert(appdata_base v) 
            {
                v2g o;
                o.pos = UnityObjectToClipPos(v.vertex);
                return o;
            }

            // 输入: point line triangle 
            // 输出: PointStream LineStream TriangleStream
            // Append
            // triStream.RestartStrip

            // 3.指定几何着色器方法内添加的顶点数
            [maxvertexcount(3)]
            void geo(triangle v2g p[3], inout LineStream stream)
            {
                g2f o;
                o.pos = p[0].pos;
                stream.Append(o);

                o.pos = p[1].pos;
                stream.Append(o);

                o.pos = p[2].pos;
                stream.Append(o);
            }

            fixed4 frag(g2f i) : SV_Target
            {
                return _WireFrameColor;
            }

            ENDCG
        }
    }
 }

#pragma target 4.0

#pragma target 4.0

#pragma target用来指定着色器的编译目标等级。
几何着色器在Shader Model 4.0及以上才支持。比如OpenGL ES2.0是不支持的,也就是说几何着色器目前基本只能在PC端使用。具体哪些平台支持Shader Model 4.0呢?可通过查看Unity官方文档,https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html

#pragma geometry geo
指定几何着色器的方法名,这没什么可说的,与顶点着色器和片元着色器一样。

[maxvertexcount(num)]
几何着色器方法名上必须添加。其用来定义几何着色器中输出顶点的最大数量,输出顶点可以每次都不同,但是不能超过这个值。

[NVIDIA08]指出,当GS输出在1到20个标量之间时,可以实现GS的性能峰值,如果GS输出在27-40个标量之间,则性能下降50%。

void geo(triangle v2g p[3], inout LineStream stream) { }
几何着色器方法,返回类型为void。
triangle v2g p[3]为输入图元,triangle表示输入图元类型为三角形。输入图元类型如下。

输入类型 描述 顶点数量
point 输入图元为点 1
line 输入图元为线 2
triangle 输入图元为三角形 3
lineadj 输入图元为带有邻接信息的直线,由4个顶点构成3条线 4
triangleadj 输入图元为带有邻接信息的三角形,由6个顶点构成 6

几何着色器输入图元
v2g为我们自定义的顶点着色器到几何着色器的结构体。

inout LineStream<g2f> stream为输出图元。inout为关键词,LineStream表示输出图元类型为线,g2f为我们自定义的几何着色器到片元着色器的结构体。 输出类型 描述
PointStream 输出图元为点
LineStream 输出图元为线
TriangleStream 输出图元为三角形

需要注意的是
每个输出的顶点都要Append到输出流中。
对于TriangleStream,Append足够的顶点数(3个)后,还需要调用一次RestartStrip,然后再继续构成下一图元。

3 几何着色器实现的效果

4 参考文章