9.在Shader中区分不同顶点

9.补充知识-如何在Shader中区分不同顶点


9.1 知识点

为何要在Shader中区分不同顶点

  • 之前存储 VAT 时,是直接得到 Mesh 网格中的顶点数据进行存储的。
  • 存储的是有序有规律的顶点数据。
  • 但是在 Shader 当中,着色器代码的执行往往是并行的。
  • 我们必须区分当前执行 Shader 代码的是哪一个顶点,才能从 VAT 中取出正确的顶点(像素)数据来使用。

如何在Shader中区分不同顶点

主要目的

  • 能够在 Shader 中知道执行当前代码的顶点索引 ID。

主要思路

  • 在顶点着色器函数中传入 SV_VertexID 语义参数。
  • SV_VertexID 语义的作用:在顶点着色器里拿到当前正在处理的是第几个顶点的索引,也就是 GPU 这次 draw call 提交的顶点流中的编号。

主要步骤

  1. 在顶点着色器的函数中,用 SV_VertexID 语义添加一个传入参数。
  2. 之后即可通过它知道顶点 ID。

具体操作

在顶点函数中添加 SV_VertexID 参数
  • 添加一个参数 vid,设置为 SV_VertexID 语义。
  • 这样就可以知道当前处理的是第几个顶点。
// ==================== 顶点着色器 ====================
// 功能:处理顶点空间转换、实例化数据传递、纹理坐标计算
// vid:添加SV_VertexID语义。可以知道是第几个顶点
v2f vert(appdata appdata, uint vid : SV_VertexID)
{
    // vid 就是"我是第几个顶点"
    // vid = 0, 1, 2, 3, ... 
    // ...
}

9.2 知识点代码

VertexIDHandler.cs

using UnityEngine;

public class VertexIDHandler 
{
    #region 知识点一 为何要在Shader中区分不同顶点

    //我们之前存储VAT时,是直接得到的Mesh网格中的顶点数据进行存储的
    //存储的是有序有规律的顶点数据
    //但是在Shader当中
    //着色器代码的执行往往是并行的
    //我们必须区分当前执行Shader代码的是哪一个顶点
    //才能从VAT中取出正确的顶点(像素)数据来使用

    #endregion

    #region 知识点二 如何在Shader中区分不同顶点

    //主要目的:
    //能够在Shader中知道执行当前代码的顶点索引ID

    //主要思路:
    //在顶点着色器函数中传入SV_VertexID语义参数
    //SV_VertexID语义的作用:
    //在顶点着色器里拿到当前正在处理的是第几个顶点的索引
    //也就是 GPU 这次 draw call 提交的顶点流中的编号

    //主要步骤:
    //在顶点着色器的函数中,用SV_VertexID语义添加一个传入参数
    //之后即可通过它知道顶点ID

    #endregion
    
}

CustomShaderToGUIInstancing.shader

// 支持GPU实例化的Unlit通用着色器
// 适用于不透明物体渲染,可通过实例化属性批量修改物体参数
Shader "Unlit/CustomShaderToGUIInstancing"
{
    // 材质面板可编辑的属性
    Properties
    {
        // 主纹理:用于物体表面的基础贴图,默认填充白色
        _MainTex ("Texture", 2D) = "white" {}
    }

    // 子着色器:核心渲染逻辑定义
    SubShader
    {
        // 渲染标签:标记为不透明物体,适配Unity渲染管线
        Tags
        {
            "RenderType"="Opaque"
        }
        // 细节级别:控制渲染距离,100为基础不透明物体标准值
        LOD 100

        // 渲染通道:执行顶点/片元渲染的核心通道
        Pass
        {
            CGPROGRAM
            // 编译指令:指定顶点着色器函数
            #pragma vertex vert
            // 编译指令:指定片元着色器函数
            #pragma fragment frag
            // 编译指令:启用雾效多版本编译,适配场景雾效
            #pragma multi_compile_fog
            // 编译指令:启用GPU实例化支持,核心功能开关
            #pragma multi_compile_instancing

            // 引入Unity内置CG函数库,包含空间转换、雾效等核心函数
            #include "UnityCG.cginc"

            // ==================== GPU实例化属性缓冲区 ====================
            // 定义实例化参数组,每个物体实例可独立修改以下参数
            UNITY_INSTANCING_BUFFER_START(MrTao)
                // 实例化颜色:每个实例独立的颜色参数
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
                // 实例化位置:每个实例独立的位置偏移参数
                UNITY_DEFINE_INSTANCED_PROP(float4, _Pos)
                // 实例化偏移索引:用于区分不同实例的序号参数
                UNITY_DEFINE_INSTANCED_PROP(int, _OffsetIndex)
            UNITY_INSTANCING_BUFFER_END(MrTao)

            // ==================== 顶点着色器输入结构体 ====================
            // 接收网格数据:顶点位置、UV、实例ID
            struct appdata
            {
                float4 vertex : POSITION;   // 模型空间顶点坐标
                float2 uv : TEXCOORD0;      // 模型第一套纹理坐标
                UNITY_VERTEX_INPUT_INSTANCE_ID // 内置宏:存储GPU实例ID(无分号)
            };

            // ==================== 顶点着色器输出结构体 ====================
            // 传递数据给片元着色器:UV、雾效、裁剪空间坐标、实例ID
            struct v2f
            {
                float2 uv : TEXCOORD0;      // 传递给片元的纹理坐标
                UNITY_FOG_COORDS(1)         // 内置宏:存储雾效计算数据(无分号!)
                float4 vertex : SV_POSITION;// 裁剪空间顶点坐标(屏幕坐标)
                UNITY_VERTEX_INPUT_INSTANCE_ID // 传递实例ID到片元着色器(无分号)
            };

            // 主纹理采样器:用于采样纹理颜色
            sampler2D _MainTex;
            // 主纹理缩放偏移:Unity自动生成,适配纹理Tiling/Offset属性
            float4 _MainTex_ST;

            
            // ==================== 顶点着色器 ====================
            // 功能:处理顶点空间转换、实例化数据传递、纹理坐标计算
            // vid:添加SV_VertexID语义。可以知道是第几个顶点
            v2f vert(appdata appdata, uint vid : SV_VertexID)
            {
                // 定义输出结构体变量
                v2f v2f;

                // 初始化实例ID:必须在访问实例化属性前调用
                UNITY_SETUP_INSTANCE_ID(appdata);
                // 传递实例ID:将顶点输入的实例ID复制到输出结构体
                UNITY_TRANSFER_INSTANCE_ID(appdata, v2f);

                // 获取当前物体实例的自定义颜色属性
                float4 color = UNITY_ACCESS_INSTANCED_PROP(MrTao, _Color);

                // 空间转换:将模型空间顶点转换为屏幕裁剪空间坐标
                v2f.vertex = UnityObjectToClipPos(appdata.vertex);
                // 纹理坐标计算:应用纹理的缩放和偏移
                v2f.uv = TRANSFORM_TEX(appdata.uv, _MainTex);
                // 雾效数据计算:传递顶点雾效参数
                UNITY_TRANSFER_FOG(v2f, v2f.vertex);

                // 将处理完成的数据传递给片元着色器
                return v2f;
            }

            // ==================== 片元着色器 ====================
            // 功能:采样纹理、应用雾效、输出最终像素颜色
            fixed4 frag(v2f v2f) : SV_Target
            {
                // 初始化实例ID:片元着色器中使用实例属性的必备步骤
                UNITY_SETUP_INSTANCE_ID(v2f);

                // 纹理采样:根据UV坐标获取主纹理的颜色
                fixed4 col = tex2D(_MainTex, v2f.uv);
                // 雾效应用:根据雾效数据混合最终颜色
                UNITY_APPLY_FOG(v2f.fogCoord, col);

                // 输出最终渲染颜色
                return col;
            }
            ENDCG
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏