仍然是跟这篇教程。
写在学之前:除了 SRP,Unity 好像没有什么东西吸引我了,从学习角度来看。
写在学之后:根本学不完。
1 自定义渲染管线
创建 CRP:
- 创建一个类继承
RenderPipelineAsset
,并 覆写其虚函数,作用类似 factory - 创建一个实例,在图形设置的 SRP 部分选中它
- 创建一个类继承
RenderPipeline
,其虚函数用于完成渲染任务- Unity 假设所有相机共用同一个上下文
- 如果假设各相机独立,可以把渲染逻辑转发给一个独立的类完成
从头写一个空的 SRP 需要注意的东西:
- 天空盒
- 各种 shader passes,SRP 不处理的那些材质
- 目前用的
SRPDefaultUnlit
是 shader tag 内置缺省值
- 目前用的
- gizmo
- UI
- 多相机(不一定,没仔细看)
ScriptableRenderContext
和 Camera
由渲染管线从引擎获取,CommandBuffer
由管线自己维护。
目前来看,相机的作用是给上下文提供一些场景相关的信息,例如变换矩阵、排序和剔除;上下文处理主要的渲染逻辑;指令缓冲负责的逻辑更偏向底层,例如 clear、设置 vp 矩阵。
上下文一般只有一个,会被用来执行多个指令缓冲。
2 Draw Call
可以看出上一回有多不专心
shader 负责实际的绘制,如果要在的 CRP 里玩连连看就得接入 Unity 的 Shader Graph。所以这部分我们来手写 HLSL。
shader 一般用来表现 material。
Unity Shader
Unity 的 shader 包括一个类似描述文件的 .shader
和若干 HLSL 文件 .hlsl
。
从这里开始就是一般 shader 的写法了,不过是上游数据来自 Unity,顶点数据、变换矩阵等等输入要和它对接好。需要的几个预置头文件在 Core RP Library 这个包里。
.shader
里面的 Properties
在编辑器里挂载了这个 shader 的材质上看。
逐材质的属性在每个物体上的行为都是一样的,逐物体不同的材质属性,可以通过给物体挂载一个(不可重复的)组件来实现。在这个组件里向编辑器暴露对应的属性,获取 shader 里对应属性的 ID,用 MaterialPropertyBlock
将这个 ID 应有的属性值传给渲染器组件。
有些硬编码要注意:.shader
里的属性字段和 .hlsl
里的 uniform 保持相同,别处需要使用它(的 ID)时,传给 Shader.PropertyToID()
的字段当然也要是一样的。
性能
主要是批处理问题,目的是解决多物体/多材质属性导致的 draw call 增加问题。
SRP Batcher
把材质属性缓存在 GPU 上,减少每次 draw call 的数据量。需要材质属性都定义在 constant buffer 里。Unity 会自行检查 shader 是否兼容 SRP batcher,并显示在 inspector 里。
实现逐物体材质属性需要的 MaterialPropertyBlock
不在 cbuffer 里面更新数据,因此 SRP batcher 不支持逐物体材质。
GPU instancing
把相同 mesh 的物体数据压缩成一个 draw call 提交,支持逐物体的属性。
具体是将逐物体的变换和材质属性收集到对应数组里打包发给 GPU,GPU 侧只需要遍历即可。
贴图和采样器是 shader 的资源,不能上 instancing。但是采样器的采样方式(缩放和偏移)可以。这样或许能通过一张大贴图配合一堆小偏移参数实现类似 instancing 的效果。
Dynamic Batching
把使用相同材质的小 mesh 组成一个大 mesh。这个合批是在 CPU 侧基于规则去做的,开销比较大,而且显然不支持逐物体的材质属性。
3 方向光
新 shader,主要的点是如何传递光源数据,这里有初步的着色思想。
材质 inspector 里的 Render Queue 可以与 filtering setting 交互,对应 shader tag 里的 "Queue"
键,详见 ShaderLab:向子着色器分配标签。subshader 和 pass 可以分配的标签不一样。
使用了 CommandBuffer
来设置 uniform 变量。
一个有点大的 shader,先构建法向量、基色、视线方向等等着色点信息,接着构建 BRDF 信息,然后就根据每个光源计算着色。着色点和 BRDF 的数据有些重复,而且目前 BRDF 是完全基于着色点信息构造的。
用了一个 metal/roughness 工作流。以后再详细讲讲 PBR 模型。
为了实现玻璃一样高光不透过但是 diffuse 透过的效果,src blend 改成 1,把 SrcAlpha 提前乘到 BRDF 里面。额外 提供了乘 alpha 的开关,注意和 src blend 选项不是正交的。
可以继承 ShaderGUI
来通过 ImGUI 的形式在 editor 里面提供控制接口,比如在代码里组合基础参数,对外则只显示为某个高级效果的开关。这块不知道能不能用属性块独立出来。