Catlike Coding | Chapter 2 Drawcalls | Blurred code

Catlike Coding | Chapter 2 Drawcalls

2022/03/08

LastMod:2022/08/17

Categories: CG

笔记栏文章声明

Warning

笔记栏所记录文章往往未经校对,或包含错误认识或偏颇观点,亦或采用只有自身能够理解的记录。

# Chapter 2 Drawcalls

Shader 基础

没什么好说的。

坐标和纹理坐标使用float,其他的都用half就足够了。 桌面端不支持真的half,都是float,移动端很讲究这个。

sementaics:

常用的hlsl

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"

要在inspector里设置变量需要在Properties里添加,并在使用时候需要先声明一个同名的变量。

Batching

drawcall需要走PCIE总线,会很慢,即使发送的数据很少。

SRP Batcher

SRP Batcher需要shader兼容,在shader的inspector里可以看到。 SRP Batcher通过缓存一些材质的属性来减少drawcall,以避免每次绘制的时候都要重新设置所有的属性。

这一块catlike没有展开讲,详细的信息在这里SRP Batcher

如图所示,Mateiral相关的属性一旦被设置就被驻留在GPU内存上,接下来的每一帧渲染CPU只会设置transform等属性,material的属性在更改的时候才会触发新的drawcall

兼容SRP Batcher的shader需要满足以下两点:

如果使用MaterialPropertyBlock是没法使用SRP Batcher的(每个物体不同属性),可以用GPU Instance。

UnityPerDraw允许的属性,最后一栏是是否允许为half(real4)。

catlike的博客里描述The exact order doesn't matter,但是实际上SRP博客里说的是The variable declaration order inside of “UnityPerDraw” CBUFFER is also important.,最好按照顺序来。(试了下不按顺序来似乎也能过)

似乎可以塞入多个属性(条件不明,似乎一个block里的变量不能随便删减)

//这个过不了
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
// float4 unity_LODFade;
real4 unity_WorldTransformParams;
CBUFFER_END

//这个能过
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
float4 unity_LightmapST;
float4 unity_lightData;
CBUFFER_END

在profile里能看见这样的栏目代表SRP Batcher启动成功,但是记得这不是某一个drawcall,而是Unity把多个连续的过程显示为一个。

Many Colors

SRP Batcher把材质的信息保存在GPU的cbuffer里。SRP Batcher能合并的材质需要其内存布局相同。 如果我们要每个物体一个颜色,需要每个物体创建一个材质,不可行。 Unity提供了一个MaterialPropertyBlock的结构体,以设置per-object material属性。 但是SRP Batcher不能对包含有MaterialPropertyBlock的物体合批。

		block.SetColor(baseColorId, baseColor);
		GetComponent<Renderer>().SetPropertyBlock(block);

GPU Instancing

使用GPU Instance可以做到带MaterialPropertyBlock的合批。 #pragma multi_compile_instancing指令必须在shader里使用,会生成不同的shader变体。

需要包含以下文件,以数组的形式定义一些变量(Unity_MATRIX_M)之类的,但是要正确支持instance我们还需要知道每个物体的index

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"

index信息在vshader阶段传入,因此我们必须把vshader的输入转换为一个结构体(正常情况下也是结构体),并定义UNITY_VERTEX_INPUT_INSTANCE_ID这个成员(macro),并且在vshader的开始的时候赋值(UNITY_SETUP_INSTANCE_ID(input);)。(This extracts the index from the input and stores it in a global static variable that the other instancing macros rely on.)

float4 UnlitPassVertex (Attributes input) : SV_POSITION {
	UNITY_SETUP_INSTANCE_ID(input);
	float3 positionWS = TransformObjectToWorld(input.positionOS);
	return TransformWorldToHClip(positionWS);
}

以上操作对fshader也适用,在v2f结构体中也需要定义index,并且在vshader中使用类似UNITY_TRANSFER_INSTANCE_ID(input, output);这种来传递index(因为index输入在vshader,需要传递给fshader)。

如果我们要支持per-instance material data,我们需要将UnityPerMaterial定义为

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
    UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)

chapter2-2022-03-08-19-19-28

Dynamic Batching

unity自带的一种可以把小的mesh合并成大的mesh的东西,用处不大。

Configuring Batching

SRP Batcher是一个类似开关,打开设置、关闭设置,是一个整个管线的设置。

DynamicBatchingGPU Instancing可以设置为逐相机的设置,其在传递给相机渲染(cameraRender)的时候被传递过去 ,最终的设置是在每帧的drawSettings里。

GraphicsSettings.useScriptableRenderPipelineBatching = useSRPBatcher;
...
// per render call
			renderer.Render(
				context, camera, useDynamicBatching, useGPUInstancing
			);

...
// in cameraRender
        var drawingSettings = new DrawingSettings(
            unlitShaderTagId, sortingSettings
        )
        {
            enableInstancing = GPU_instancing,
            enableDynamicBatching = dynamic_batch
        };

另外支持GPU Instancing 的材质的inspector上也会有一个开关,关掉后这个material的渲染不会触发instance。

Transparency

Pass前使用Blend可以调节混合模式

		Pass {
			Blend [_SrcBlend] [_DstBlend]

			HLSLPROGRAM
			…
			ENDHLSL
		}

No writing Depth

ZWrite [_ZWrite]在Pass前控制深度写入,一般透明物体不写深度(但是要做深度测试),因为透明物体能看见后面的物体,写深度会导致后面的物体被剔除。

Textureing

声明纹理和采样器,名字要注意匹配。

TEXTURE2D(_BaseMap); 
SAMPLER(sampler_BaseMap);

如果在GPU Instance里要使用_BaseMap_ST,要声明在GPU Instance的变量区域里。

UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)

Alpha Clipping

在Shader里可以使用clip(value)函数丢弃value <= 0的像素,比如 clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));,丢弃比_Cutoff小的向量。

Shader Features

可以通过#pragma shader_feature xxx定义shader变体,会生成两个shader(指数级上升)。

生效的话,可以通过[Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 0这样的声明,Toggle这个关键词会调用不同的变体,也可以在C#端手动启用keyword