几何着色器 Geometry Shader

参考: 

【Unity Shader入门】4、几何着色器Geometry Shaders之结构解析_shader编译目标级别-CSDN博客

Geometry Shader(Unity几何着色器)_unity geometry shader-CSDN博客

Unity几何着色器详解 - 知乎 (zhihu.com)

 

概述

在顶点着色器和片元着色器之间有一个可选的几何着色器(geometry shader) 阶段。 几何着色器可以将输入图元转换为其他图元,这是曲面细分等阶段无法左到的。

几何着色器在输入的几何图元(如点、线段、三角形)级别上进行操作,并根据需要生成零个、一个或多个输出的新的几何图元。

这使得在几何着色器阶段可以实现诸如几何图元的细分、几何的放大缩小、草地生成、粒子系统等各种复杂的效果。

几何着色器的输入是完整的图元,输出是新的图元

顶点着色器以顶点数据作为输入数据,而几何着色器的输入是上一个阶段生成的顶点组成的图元。与顶点着色器不同,几何着色器可以创建或销毁几何图元。然后,几何着色器将新的几何图元传递给光栅化阶段。经过光栅化处理后,最终会生成像素片段,供像素着色器阶段进行处理。

需要图形API,Vulkan 或 DX11以上 或 OpenglES3.2以上 才支持,不过目前来看,低端的红米手机都能跑

/* 
    [maxvertexcount(N)]:用来指定几何着色器单次调用所输出的顶点数量最大值。
    其中,N是几何着色器单次调用所输出的顶点数量最大值。几何着色器每次输出的顶点个数都可能不同,但是这个数量不能超过之前定义的最大值。
    出于对性能方面的考虑,我们应当令maxvertexcount的值尽可能小。线管资料显示,GS着色器每次输出的标量数量在1-20时,它将发挥出最佳的性能;而当27-40时,它的性能将下降到峰值性能的50%。
    每次调用几何着色器所输出的标量个数为:maxvertexcount与输出顶点类型结构体中标量个数的乘积。
    例如,如果顶点结构体定义了float3 pos : POSITION与float2 tex : COORD0,即顶点元素中含有5个标量。
    假设此时将maxvertexcount设置为4,则几何着色器每次输出20个标量,以峰值性能执行。
*/
[maxvertexcount(3)] 
/*
    *  Input: PrimitiveType InputVertexType InputName[NumElements]
        PrimitiveType:表示输入的图元类型,我们一般都是处理基于3角形的3D模型,一般选三角形,可以有以下几种:
            point:输入图元为点,顶点数量1
            line:输入图元为线,顶点数量2
            triangle:输入图元为三角形,顶点数量3
            lineadj:输入图元为带有邻接信息的直线,由4个顶点构成3条线,顶点数量4
            triangleadj:输入图元为带有邻接信息的三角形,由6个顶点构成,顶点数量4
        InputVertexType:表示输入的结构体,例如v2g,在代码里定义
        NumElements:表示输入的顶点数据元素数量,根据上面的输入图元类型填对应的顶点数量
        
    *  Inout: StreamOutputObjectVertexType<OutputVertexType> OutputName
        StreamOutputObjectVertexType:表示输出的顶点类型,可以有:
            PointStream:输出图元为点,即最终模型只显示顶点
            LineStream:输出图元为线,即最终模型只显示线框
            TriangleStream:输出图元为三角形,普通的模型面显示方式
        OutputVertexType:表示输出的顶点类型,例如g2f,在代码里定义
    */
void geom(triangle v2g p[3], inout TriangleStream<g2f> tStream)
{
    for (int i = 0; i < 3; i++) 
    {
        // 定义一个g2f输出结构体,并将参数都初始化为0
        g2f o = (g2f)0;
        o.pos = p[i].pos;
        o.normal = p[i].normal;
        o.uv  = p[i].uv ;
        // 将每个顶点添加到TriangleStream流里
        tStream.Append(o);
    }
    // 如果是添加三角面,每输出点足够对应相应的图元后,都要RestartStrip()一下再继续构成下一图元,其他两种输出模式不需要这步骤
    tStream.RestartStrip();
}

 

一个例子,在每个三角形的每个顶点周围均匀分布生成10个裂变顶点,并且随时间变化在原位置上移动:

[maxvertexcount(30)]
void geom(triangle v2g p[3], inout PointStream<g2f> pStream)
{
    float time = _Time.y; // 获取当前时间

    // 对每个三角形的每个顶点进行裂变
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 10; ++j)
        {
            g2f o;
            // 在三角形顶点周围均匀分布生成新顶点
            float phi = 2 * 3.14159265359 * float(j) / 10.0f;
            float r = 0.1f; // 裂变顶点的半径,可以根据需求调整

            // 计算新顶点位置,同时考虑时间变化
            float3 offset = float3(cos(phi), sin(phi), 0) * r;
            float movement = sin(time + phi * 2) * 0.02f; // 时间因子,用于随时间变化
            o.pos = p[i].pos + float4(offset * (1.0 + movement), 0);

            // 使用原始顶点的法线和纹理坐标
            o.normal = p[i].normal;
            o.uv = p[i].uv;

            // 添加到输出流
            pStream.Append(o);
        }
    }
}

 

 一个完整的Shader例子,接受输入为三角形面的三个顶点,分别输出为点、线、面三个函数:

Shader "Custom/GeometryShader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Color ("Main Color", Color) = (1,1,1,1)
    }

    SubShader
    {
        Pass
        {
            Tags{ "RenderType" = "Opaque" }
            LOD 200

            CGPROGRAM
            #pragma vertex vert
            // 这里指明要使用一个名为geom的几何着色器
            #pragma geometry geom
            #pragma fragment frag
            #include "UnityCG.cginc"

            uniform sampler2D _MainTex;
            fixed4 _Color;

            // 定义输入结构体,用于顶点着色器输出,几何着色器输入
            struct v2g
            {
                float4  pos     : POSITION;
                float3  normal  : NORMAL;
                float2  uv      : TEXCOORD0;
            };

            // 定义输出结构体,用于几何着色器输出,片段着色器输入
            struct g2f
            {
                float4  pos     : POSITION;
                float3  normal  : NORMAL;
                float2  uv      : TEXCOORD0;
            };

            // 顶点着色器
            v2g vert(appdata_base v)
            {
                v2g o = (v2g)0;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv  = v.texcoord;
                return o;
            }

            // 几何着色器
            // 将模型输出为点
            [maxvertexcount(3)] 
            void geom(triangle v2g p[3], inout PointStream<g2f> pStream)
            {
                for (int i = 0; i < 3; i++) 
                {
                    g2f o = (g2f)0;
                    o.pos = p[i].pos;
                    o.normal = p[i].normal;
                    o.uv  = p[i].uv ;
                    //将每个顶点添加到PointStream流里
                    pStream.Append(o);
                }
            }

            // [maxvertexcount(3)] 
            // 将模型输出为线框
            // void geom(triangle v2g p[3], inout LineStream<g2f> lStream)
            // {
            //     for (int i = 0; i < 3; i++) 
            //     {
            //         g2f o = (g2f)0;
            //         o.pos = p[i].pos;
            //         o.normal = p[i].normal;
            //         o.uv  = p[i].uv ;
            //         //将每个顶点添加到LineStream流里
            //         lStream.Append(o);
            //     }
            // }

            // [maxvertexcount(3)] 
            // 将模型输出为三角面
            // void geom(triangle v2g p[3], inout TriangleStream<g2f> tStream)
            // {
            //     for (int i = 0; i < 3; i++) 
            //     {
            //         g2f o = (g2f)0;
            //         o.pos = p[i].pos;
            //         o.normal = p[i].normal;
            //         o.uv  = p[i].uv ;
            //         //将每个顶点添加到TriangleStream流里
            //         tStream.Append(o);
            //     }
            //     //添加三角面
            //     //每输出点足够对应相应的图元后
            //     //都要RestartStrip()一下再继续构成下一图元
            //     tStream.RestartStrip();
            // }

            // 片段着色器
            fixed4 frag(g2f i) : COLOR
            {
                return tex2D(_MainTex, i.uv) * _Color;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

posted @ 2024-07-10 19:28  JeasonBoy  阅读(127)  评论(0编辑  收藏  举报