|
四、 SRP Shader
在上一章节中,简单定义了能够处理半透明物体的管线,SRP几乎控制了整个渲染处理框架。因此SRP的Shader与内置管线Shader,在书写上会有一些差异。下面介绍一下SRP Shader与内置Shader的差异。
4.1 ShaderLab差异
内置管线默认使用GLSL来处理Shader,而SRP使用HLSL来处理,因此SRP Shader使用HLSLPROGRAM和ENDHLSL 来标记Shader逻辑区域。
SRP Shader可以通过引用hlsl文件的方式,将处理逻辑从ShaderLab中分离。
4.2 HLSL与内置变量
从上述调用关系来看,ShaderLab中定义的变量以及全局变量,需要在hlsl文件中再次定义。(ShaderLab可以看作连接编辑器的壳)
CBuff
HLSL提供了cbuff的方式以实现类似常量的功能。这些数据不可更改、访问延迟低、允许CPU频繁更新。只需要为变量添加cbuffer关键字就可以创建常量缓冲区。这也是SRP Batch的关键!
cbuffer UnityPerFrame {
float4x4 unity_MatrixVP;
};
cbuffer UnityPerDraw {
float4x4 unity_ObjectToWorld;
}但是并不是所有平台都支持常量缓冲区,为了处理平台不支持的情况,SRP的Common.hlsl提供了CBUFFER_START,实现仅在需要该cBuff时使用它们。
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
CBUFFER_START(UnityPerFrame)
float4x4 unity_MatrixVP;
CBUFFER_END
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
CBUFFER_END下面给出一些常用的Unity内置变量:
float4x4 unity_ObjectToWorld;//M变换矩阵
float4x4 unity_WorldToObject;//Mt变换矩阵
float4x4 unity_MatrixVP;//VP变换矩阵
float4x4 unity_MatrixV;//V变换矩阵
float4x4 glstate_matrix_projection;// 投影矩阵五、Batch
5.1 SRP Batch
在上一篇介绍过SRP Batch优化方式:对于相同Shader变体,只进行一次GPU状态设置,以减少GPU处理耗时。SRP Batch示例:创建两组Cube,每组8个Cube,此时Batch为18,SetPass为6。
未进行SRP Batcher处理的性能
要实现SRP Batcher需要将两部分数据存储到特定cbuff中:材质参数与空间Transform数据分别存储到UnityPerMaterial和UnityPerDraw cbuff中。因此我们对Shader和管线做以下处理做以下处理:
// Shader
CBUFFER_START(UnityPerMaterial)
float4 _MainColor;
CBUFFER_END
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
CBUFFER_END
// 管线
public FirstPipeline()
{
GraphicsSettings.useScriptableRenderPipelineBatching = true;
}此时在编辑器中选中Shader,可以看到SRP Batcher已编译成功,SetPass变为3。注:SRP无法兼容MaterialPropertyBlock输入参数数组。
5.2 GPU Instance & Graphics.DrawMeshInstanced
SRP Batcher虽然降低了SetPass的次数,但并不影响Batch:CPU向GPU提交数据的次数。上面例子中只使用了两种材质,GPU Instance可以对相同材质数据进行批量提交。
首先需要在Shader中添加以下编译指令,此时可以看到Shader对应的Material多了一个GPU Instance可选项:
#pragma multi_compile_instancing
#pragma vertex vert
#pragma fragment frag
新建Common.hlsl用于引入GPU Instance所需要的外部文件
#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "UnityInput.hlsl"
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
#endif将材质数据定义在UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)与UNITY_INSTANCING_BUFFER_END(UnityPerMaterial),变量通过UNITY_DEFINE_INSTANCED_PROP处理
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)对于变量的定义也有所不同,需要在结尾部分添加UNITY_VERTEX_INPUT_INSTANCE_ID:
struct Attributes {
float3 positionOS : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings {
float4 positionCS : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Shader中获取传入数据需要UNITY_SETUP_INSTANCE_ID处理,获取材质数据需要通过UNITY_ACCESS_INSTANCED_PROP处理。
Varyings UnlitPassVertex(Attributes input) {
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
...
}
float4 UnlitPassFragment(Varyings input) : SV_TARGET {
UNITY_SETUP_INSTANCE_ID(input);
float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
return baseColor;
}当完成以上操作后我们发现Batches与SetPass与初始相比,有了极大的优化:
Unity提供了Graphics.DrawMeshInstanced来直接绘制GPU Instance材质。
if (block == null)
{
block = new MaterialPropertyBlock();
block.SetVectorArray(baseColorId, baseColors);
}
Graphics.DrawMeshInstanced(mesh, 0, material, matrices, 1023, block);
参考
- Catlike Coding - Custom SRP
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|