9.补充知识-如何在Shader中区分不同顶点
9.1 知识点
为何要在Shader中区分不同顶点
- 之前存储 VAT 时,是直接得到 Mesh 网格中的顶点数据进行存储的。
- 存储的是有序有规律的顶点数据。
- 但是在 Shader 当中,着色器代码的执行往往是并行的。
- 我们必须区分当前执行 Shader 代码的是哪一个顶点,才能从 VAT 中取出正确的顶点(像素)数据来使用。
如何在Shader中区分不同顶点
主要目的
- 能够在 Shader 中知道执行当前代码的顶点索引 ID。
主要思路
- 在顶点着色器函数中传入
SV_VertexID语义参数。 SV_VertexID语义的作用:在顶点着色器里拿到当前正在处理的是第几个顶点的索引,也就是 GPU 这次 draw call 提交的顶点流中的编号。
主要步骤
- 在顶点着色器的函数中,用
SV_VertexID语义添加一个传入参数。 - 之后即可通过它知道顶点 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