sonycn01 发表于 2024-7-15 18:20

Unity SRP从零搭建一套图形衬着管线 实践2

参照 UWA上的一个教程:Unity SRP从零搭建一套图形衬着管线_UWA学堂 (uwa4d.com)
这是第二章节
以下图文均来自上面的教程,一些具体法式做了简略,参考教程即可,这里仅记录一些常识点。


1. Shader

SRP 只保留了对Unlit Shader的撑持,本节我们开始编写本身的Shader来衬着
Unity 内置的衬着管线着色器使用的是CG语言,和HLSL斗劲像,但是目前CG语言遏制更新很久了,基本已经被放弃,所以SRP使用了HLSL语言。
1.1 Unlit Shader

这个小节不涉及光照。参照教程创建Shader和hlsl文件。
Shader中include hlsl文件


HLSL文件里面加上
#ifndef CUSTOM_UNLIT_PASS_INCLUDED
#define CUSTOM_UNLIT_PASS_INCLUDED
#endif
我们一般通过#define指令定义一些标识符,在定义宏之前先判断一下是否认义过此标识符,如果定义过了,就跳过里面的所有代码不再执行,直接跳转到#endif末尾的代码。这样就能保证无论反复Include该HLSL文件多少次,只有第一次的Include是有效代码插入
1.2 着色器函数

先创建如下着色器


UnityInput.hlsl 文件存储Unity提供的一些尺度输入,定义一个模型空间到世界空间的转换矩阵。定义一个Common.hlsl文件存储,模型空间转到世界空间的方式。






上面顶点颠末模型转世界,世界转裁剪之后,网格显示正常了。


1.3 SRP 源码库

以上提及的在Common.hls定义的两空间转换方式斗劲常用,在安装的插件包Core RP Library中也有官方的库文件定义了这两个方式,所以我们把本身定义的TransformObjectToWorld和TransformWorldToHClip方式删除,替换为官方的,然后补全片元函数,这里代码较多就不贴了。 成果如下:


2.批措置

2.1 Draw Call 和Set Pass Call

命令缓冲区的命令有很多种类,而Draw Call就是此中一种,其它命令还有Set Pass Call等等。Set Pass Cal代表了我们常说的改变染状态,当切换材质或者切换同一材质中Shader的分歧Pass进行染时城市发一次Set Pass Cal。比如我们染1000个不异的物体和染1000个分歧的物体,虽然两者Draw Cal都是1000,但是前者Set Pass Cal为1,后者还是1000切换染状态在往比Draw Cal更耗时,所以这也是URP不再撑持多Pass的原因
可参考:Unity3D之DrawCalls、Batches和SetPassCalls的关系_Wei_Yuan_2012的博客-CSDN博客
2.2 SRP Batcher

SRP Batcher是一种新的批措置方式,它不会减少Draw Call的数量,但可以减少Set Pass Call的数量,并减少绘制调用命令的开销。CPU不需要每帧都给GPU发送衬着数据,如果这些数据没有发生变化则会保留在GPU内存中,每个绘制调用仅需包含一个指向正确内存位置的偏移量。
SRP Batcher是否会被打断的判断依据是Shader变种,即使物体之间使用了分歧的材质,但是使用的Shader变种不异就不会被打断,传统的批措置方式是要求使用同一材质为前提的。
SRP Batcher会在主存中将模型的坐标信息、材质信息、主光源暗影参数和非主光源暗影参数分袂保留到分歧的CBUFFER(常量缓中区)中,只有CBUFFER发生变化才会从头提交到GPU并保留。
如何判断Shader是否兼容 SRP Batcher,通过下面的属性看,当前的Shader是不兼容的


如下代码插手CBUFFER后,就可以兼容了






有关Cbuffer
【Unity】SRP底层衬着流程及道理 - 知乎 (zhihu.com)
hlsl 中使用的常量缓冲区(cbuffer)究竟是什么? | (1r1g.com)
使用GraphicsSettings.useScriptableRenderPipelineBatching = true; 开启SRP合批
2.3 多种颜色

编写一个脚本使得同一个材质的物体能有分歧的颜色,否则默认是会颜色共享的。
参考:Unity材质属性块MaterialPropertyBlock - 简书 (jianshu.io)
我们新建如下脚本,挂在3个球体上:


会发现挂了脚本的SRP Batch 掉效了


2.4 GPU instancing

1)要撑持GPU Instancing,首先需要在Shader的Pass中添加#pragma multi_compile_instancing指令,然后在材质球上就能看到切换开关了,这时Unity会为我们的Shader生成两种变体,此时材质上也能看到开关了。


2)在Common.hlsl中插手
#include ”Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl”
3)改削顶点函数、片段函数


至此,4个球合并为1个DrawCall


一些说明
Unitylnstancing.hls通过从头定义一些宏去访谒实例的数据数组,它需要知道当前染对象的索引,该索引是通过顶点数据提供的。Unityinstancing.hlsl中定义了UNITY_VERTEX_INPUT_INSTANCE_ID宏来简化了这过程。
UNITY_VERTEX_INPUT_INSTANCE_ID 从顶点数据中获取当前衬着对象的索引
UNITY_INSTANCING_BUFFER_START
UNITY_DEFINE_INSTANCED_PROP
UNITY_SETUP_INSTANCE_ID
UNITY_TRANSFER_INSTANCE_ID
2.5 绘制许多网格小球

创建一个空物体,挂上如下脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 生成1023个mesh和小球对象
/// </summary>
public class MeshBall : MonoBehaviour
{
    static int baseColorId = Shader.PropertyToID(”_BaseColor”);
   
    Mesh mesh = default;
   
    Material material = default;

    Matrix4x4[] matrices = new Matrix4x4;
    Vector4[] baseColors = new Vector4;


    MaterialPropertyBlock block;

    void Awake()
    {
      for (int i=0;i<matrices.Length;i++)
      {
            //创建随机转换矩阵和颜色
            matrices = Matrix4x4.TRS(Random.insideUnitSphere*10f, Quaternion.Euler(
                  Random.value * 360f, Random.value * 360f, Random.value * 360f
                ),
                Vector3.one * Random.Range(0.5f, 1.5f));
            baseColors = new Vector4(Random.value,Random.value,Random.value,Random.Range(0.5f, 1f));
      }
    }

   void Update()
    {
      if (block == null)
      {
            //随机属性发送到着色器
            block = new MaterialPropertyBlock();
            block.SetVectorArray(baseColorId, baseColors);
      }   
               //绘制网格实例
      Graphics.DrawMeshInstanced(mesh,0,material,matrices,1023,block);
    }
}
即可生成大量小球。


2.6 动态合批

使用如下代码打开批措置开关,即可开启合批和GPUInstance
//绘制设置,设置衬着的Shader Pass 和排序模式
var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings)
{
//设置衬着时批措置的使用状态
enableDynamicBatching = useDynamicBatching,
enableInstancing = useInstancing,
   };3.Alpha Blend 和 Alpha Test

3.1 Blend Modes


在Shader中设置混合模式,目前设置的是尺度的不透明混合模式




有关混合的参考:
Unity-ShaderLab:混合 - 哔哩哔哩 (bilibili.com)
注意透明物体衬着要封锁深度写入,否则得不到正确成果
最终参照如下配置


3.2 材质添加对纹理的撑持

增加如下代码






这块和以前的管线没啥大区别,直接贴出成果。


3.3 透明度测试 Alpha Test

附上代码






材质凡是使用透明度测试和透明度混合此中一个,而不是同时使用。透明度测试应使用在完全不透明的物体身上,除了被clip丢弃的片元外,其它片元会写入深度缓冲中。我们把混合模式设置成尺度不诱明物体的配置,然后开启深度写入,衬着队列设置为AlphaTest。


3.4 Shader Feature

使用shader feature可以让Unity按照分歧的定义条件或关键字编译多次,生成多个着色器变体。然后通过外部代码或者材质面板上的开关来启用某个关键字,加载对应的着色器变种版本来执行某些特定功能,是项目开发中斗劲常用的一种手段。下面我们添加一个控制透明度测试功能是否启用的开关。








3.5 逐对象的裁剪

还是通过MaterialPropertyBlock

页: [1]
查看完整版本: Unity SRP从零搭建一套图形衬着管线 实践2