SRP_02_DrawCalls


一、SRP Batcher

简述:

  • 将一组参数打包放在 GPU 上,渲染时不用切换渲染状态,从而进行渲染优化。但不能减少 DrawCall,只是降低 DrawCall 的代价

合批限制:

  • Property内的属性,都要写到 CBuffer 内
  • 全局变量不能写在 CBuffer 内
  • CBuffer内不包含#if(非常量大小CBuffer)
  • CBuffer内的变量必须按照half4 half2 half 从上到下的顺序写,顺序不能乱不能穿插的写,不然就会闪退,还有不能写half3,要用half4代替half3,不然在某些机器上会显示错误。
  • 一个 Shader 里面有多个 Pass,每个 Pass 的 CBuffer 应该一致

缺点:

  • SRP Bather 不支持 MaterialPropertyBlock,如果有很多个不同属性但使用相同Shader的需求,只能创建 N 多个材质,然后进行 SRP Bather
    • Instance 支持 MaterialPropertyBlock,所以上面的情况,可以用 Instance + MaterialPropertyBlock 代替

二、GPU Instancing

简介:

  • GPU Instancing 支持 MaterialPropertyBlock,如果有使用相同 Shader 但是不同材质属性的需求,GPU Instancing 可以进行高效处理

缺点:

  • 一般来说,GPU实例化比动态批处理效果更好。这种方法也有一些注意事项,例如,当涉及到不同比例时,较大的网格的法向量不能保证是单位长度的。另外,绘制顺序也会发生变化,因为现在是单个网格而不是多个。

使用方式:

  • 管线
SortingSettings sortingSettings = new SortingSettings(_camera)
{
    criteria = SortingCriteria.CommonOpaque
};

DrawingSettings drawingSettings = new DrawingSettings(UnlitShaderTagId, sortingSettings)
{
    enableInstancing = useGPUInstancing, //启用GPUInstancing
};
drawingSettings.SetShaderPassName(1, LitShaderTagId);

FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque, renderingLayerMask: (uint) renderingLayerMask);
_context.DrawRenderers(_cullingResults, ref drawingSettings, ref filteringSettings);
  • 宏定义引用文件
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
  • 属性定义
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
UNITY_DEFINE_INSTANCED_PROP(float4, _EmissionColor)
UNITY_DEFINE_INSTANCED_PROP(float4, _MainTex_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _DetailMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float, _DetailAlbedo)
UNITY_DEFINE_INSTANCED_PROP(float, _DetailSmoothness)
UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_DEFINE_INSTANCED_PROP(float, _ZWrite)
UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)
UNITY_DEFINE_INSTANCED_PROP(float, _Occlusion)
UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
UNITY_DEFINE_INSTANCED_PROP(float, _Fresnel)
UNITY_DEFINE_INSTANCED_PROP(float, _NormalScale)
UNITY_DEFINE_INSTANCED_PROP(float, _DetailNormalScale)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
  • 结构定义
struct Attributes
{
    float4 positionOS : POSITION;
    float2 uv : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
    float4 positionCS_SS : SV_POSITION;

    float2 uv : VAR_UV;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};
  • Vertex 和 Fragment 使用
Varyings LitPassVertex(Attributes input)
{
    Varyings output = (Varyings)0;

    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);

    output.positionCS_SS = TransformWorldToHClip(output.positionWS);
    
    return output;
}

float4 LitPassFragment(Varyings input) : SV_TARGET
{
    UNITY_SETUP_INSTANCE_ID(input);
    
    float4 finalColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Color)
    return finalColor;
}
  • 材质球

    image-20221117164846483
  • C# 端

using UnityEngine;
using UnityEngine.Rendering;
using Random = UnityEngine.Random;

namespace CustomRP.Examples
{
    public class MeshBall : MonoBehaviour
    {
        private static int _colorID = Shader.PropertyToID("_Color");
        private static int _metallicID = Shader.PropertyToID("_Metallic");
        private static int _smoothnessID = Shader.PropertyToID("_Smoothness");

        [SerializeField] private Mesh mesh = default;
        [SerializeField] private Material material = default;
        [SerializeField] private LightProbeProxyVolume _lightProbeProxyVolume = null;

        private Matrix4x4[] _matrices = new Matrix4x4[1023];
        private Vector4[] _colors = new Vector4[1023];

        private float[]
            _metallic = new float[1023],
            _smoothness = new float[1023];

        private MaterialPropertyBlock _block;

        private void Awake()
        {
            for (int i = 0; i < _matrices.Length; i++)
            {
                _matrices[i] = Matrix4x4.TRS(
                    Random.insideUnitSphere * 10f,
                    Quaternion.Euler(Random.value * 360f, Random.value * 360f, Random.value * 360f),
                    Vector3.one * Random.Range(0.5f, 1.5f));
                _colors[i] = new Vector4(Random.value, Random.value, Random.value, Random.Range(0.5f, 1.5f));
                _metallic[i] = Random.value < 0.25f ? 1f : 0f;
                _smoothness[i] = Random.Range(0.05f, 0.95f);
            }
        }

        private void Update()
        {
            if (_block == null)
            {
                _block = new MaterialPropertyBlock();
                _block.SetVectorArray(_colorID, _colors);
                _block.SetFloatArray(_metallicID, _metallic);
                _block.SetFloatArray(_smoothnessID, _smoothness);

                if (_lightProbeProxyVolume == null)
                {
                    Vector3[] positions = new Vector3[1023];
                    for (int i = 0; i < _matrices.Length; i++)
                    {
                        positions[i] = _matrices[i].GetColumn(3);
                    }

                    SphericalHarmonicsL2[] lightProbes = new SphericalHarmonicsL2[1023];
                    Vector4[] occlusionProbes = new Vector4[1023];
                    LightProbes.CalculateInterpolatedLightAndOcclusionProbes(positions, lightProbes, occlusionProbes);
                    _block.CopySHCoefficientArraysFrom(lightProbes);
                    _block.CopyProbeOcclusionArrayFrom(occlusionProbes);
                }
            }

            Graphics.DrawMeshInstanced(
                mesh, 0, material, _matrices, 1023, _block,
                ShadowCastingMode.On, true, 0, null,
                _lightProbeProxyVolume ? LightProbeUsage.UseProxyVolume : LightProbeUsage.CustomProvided,
                _lightProbeProxyVolume);
        }
    }
}

三、Dynamic Batching

简述:

  • 将使用相同材质球的多个小Mesh合并成一个大Mesh,从而减小 DrawCall

缺点:

  • 不支持 MaterialPropertyBlock

使用方式:

  • 管线
SortingSettings sortingSettings = new SortingSettings(_camera)
{
    criteria = SortingCriteria.CommonOpaque
};

DrawingSettings drawingSettings = new DrawingSettings(UnlitShaderTagId, sortingSettings)
{
    enableDynamicBatching = useDynamicBatching, //启用动态批处理
};
drawingSettings.SetShaderPassName(1, LitShaderTagId);

FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque, renderingLayerMask: (uint) renderingLayerMask);
_context.DrawRenderers(_cullingResults, ref drawingSettings, ref filteringSettings);
  • 禁用 SRP,他的优先级比较高
GraphicsSettings.useScriptableRenderPipelineBatching = false;

四、AlphaTest

简述:

  • 是一个典型的不透明物体,也会写入深度,只是有部分像素被丢弃,队列在2450,在不透明之后半透明之前渲染。因为它丢弃了像素,所以不能保证在它后面的物体是否会渲染出来,因此为了性能优化,渲染队列在不透明之后半透明之前渲染

文章作者: 血魂S
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 血魂S !
  目录