URP 中的内置 GPU Instancing 的使用,和 Built-in RP 之前的宏定义名字是一样的,而且功能也是一样的,所以:使用方法和 Built-in RP 中没任何却别
Shader
下面代码搜索查看:// jave.lin instancing
相关部分
// jave.lin 2021/10/18
// 测试 URP Instancing 的功能
Shader "Test/URPGPUInstancing"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#define FOG_LINEAR // 我们可以使用明文声明的方式来确定变体
//#pragma multi_compile_fog // jave.lin fog 变体 keyword 需要,这里注释掉,不用这种方式
#pragma multi_compile_instancing // jave.lin instancing 定义 INSTANCING_ON 变体开关
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // jave.lin fog ComputFogFactor, MixFog 等,雾效相关的 API 在里面有
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl" // jave.lin instancing
CBUFFER_START(UnityPerMaterial) // SRP Batcher 用的 cbuffer,但是 instancing buffer 定义了的话,SRP Batcher 将不会合批来减少 SetPassCall
float4 _MainTex_ST;
CBUFFER_END
UNITY_INSTANCING_BUFFER_START(Props) // jave.lin instancing 不能和 srp batcher 共存,如果要有定义 instancing 的 buffer 内容,shader inspector SRP Batcher 将显示 : not compatible
UNITY_DEFINE_INSTANCED_PROP(half4, _Color) // jave.lin instancing 定义 PropsArray[N]._Color;
UNITY_INSTANCING_BUFFER_END(Props) // jave.lin instancing 定义 PropsArray 中的属性
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID // jave.lin instancing 定义 uint instanceID : SV_InstanceID;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // jave.lin instancing 定义 uint instanceID : SV_InstanceID;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
v2f vert (appdata v)
{
v2f o = (v2f)0;
UNITY_SETUP_INSTANCE_ID(v); // jave.lin instancing 设置 static uint unity_instanceID;
UNITY_TRANSFER_INSTANCE_ID(v, o); // jave.lin instancing 给 v2f o 设置 uint instanceID : SV_InstanceID;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// jave.lin instancing 根据 instanceID 来取 _MainTex_ST 值,但是
//half4 uv_scale_offset = UNITY_ACCESS_INSTANCED_PROP(Props, _MainTex_ST);xxx_ST 是 application 层封装了的黑箱处理,只能设置普通 uniform 值,而不是 cbuffer 中的数值,所以这里如果是从 instancing buffer 中去 _MainTex_ST 将会是去不到的
//o.uv = uv_scale_offset.xy * o.uv + uv_scale_offset.zw;
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); // 设置 static uint unity_instanceID
float csz = i.vertex.z * i.vertex.w; // jave.lin fog 因为 vs 阶段后 还有一个 vs post 会处理 vertex data 的 perspective dive 的透视除法,所以这里需要将除去的值重新还原到 clip space z
real fogFactor = ComputeFogFactor(csz); // jave.lin fog 根据 clip space z 来计算 fog factor
half4 tintCol = UNITY_ACCESS_INSTANCED_PROP(Props, _Color); // jave.lin instancing 根据 instanceID 来取 _Color 值
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv) * tintCol;
col.rgb = MixFog(col.rgb, fogFactor); // jave.lin fog 根据 fogFactor lerp 混合 src color & fog color
return col;
}
ENDHLSL
}
}
}
效果
但是,如同注释中描述,开启 instancing ,那么 SRP Batcher 将不生效,如下图:
选用 SRP Batcher 还是 Instancing?
在之前的文章中有说到:Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影
有两个选择:那么要 instancing? 还是 SRP Batcher 呢?这要看你自己去权衡,但是这里有一些建议:
- 如果大量绘制的网格都一致,那么建议用 instancing
- 如果用上 instancing,那么你的其他 pass 最好都同步添加上 instancing 的支持
- 如果网格不一致,但是 shader ,且 shader 变体一致,那么建议用 SRP Batcher
如何 SRP Batcher 主要是将 uniform 变量都划分到对应的 CBuffer 块中,可以查看我之前一篇:Unity Shader - shader lab 的 SRP Batcher compatible 兼容性
References
- 从零实现Unity通用渲染管线(URP)三:合批(Batch) - 里面有讲解到 URP GPU Instancing 的代码,其实我之前看 URP Shader 源码也发现了