Built-in / URP / HDRP:写法差异与差异的原因
同一份「手写光照」在 Built-in 里能跑、在 URP 里全黑,多半是 Pass 标签和头文件的问题。这篇给三管线的对照与迁移清单——先讲差异为什么存在,清单才记得住。
三种管线是什么关系
- Built-in:老管线,光照路径半固定(Vertex/Forward/Base 等 Pass 由引擎按内置规则挑选),shader 库是 CG 系(
UnityCG.cginc)。 - URP / HDRP:SRP(Scriptable Render Pipeline)——渲染流程本身是 C# 代码,shader 与管线的约定改成显式协议。URP 面向全平台(移动/主机/PC),HDRP 面向高端。
- 差异的总根源:SRP 把「引擎隐式做的事」变成「显式约定」——挑 Pass 看标签、传参数看 CBUFFER、算光照看 include 库。约定更严格,换来的是管线可定制与批处理优化。
对照表:迁移时逐项核对
| 项 | Built-in | URP |
|---|---|---|
| 代码块 | CGPROGRAM | HLSLPROGRAM(CG 语法不支持) |
| 基础库 | UnityCG.cginc / Lighting.cginc | Universal/Core.hlsl / Lighting.hlsl / CommonMaterial.hlsl |
| 采样 | tex2D(_T, uv) | SAMPLE_TEXTURE2D(_T, sampler_T, uv)(需 TEXTURE2D+SAMPLER 声明) |
| 变换 | UnityObjectToClipPos | TransformObjectToHClip(后缀 OS/WS/VS/CS 命名约定) |
| 主光 | _WorldSpaceLightPos0、_LightColor0 | GetMainLight() 返回 Light 结构 |
| Pass 挑选 | LightMode 部分隐式(ForwardBase 等) | SRP 按 LightMode 标签挑 Pass:UniversalForward、ShadowCaster、DepthOnly、DepthNormals、SceneSelection… 标签错 = 该功能缺这个物体 |
| 材质参数 | 零散 uniform 即可 | 全部进 CBUFFER_START(UnityPerMaterial),否则 SRP Batcher 不兼容(见 /notes/unity/structure-4/) |
| 阴影 keyword | multi_compile_fwdbase | _MAIN_LIGHT_SHADOWS / _MAIN_LIGHT_SHADOWS_CASCADE / _ADDITIONAL_LIGHTS 等显式列表 |
| 多灯 | ForwardBase + ForwardAdd 多遍 | 单 Pass 内循环(灯数据按 tile 批量进 CBUFFER) |
| 附加 Pass | 通常不用写 | 要投影/深度效果就得自己提供 ShadowCaster/DepthOnly/DepthNormals Pass |
为什么 URP 强制 LightMode 标签
SRP 的 C# 代码按「渲染功能」组织:画深度图时,它需要找到每个材质专门负责深度的 Pass——靠 LightMode = DepthOnly 标签找。Built-in 里引擎用一堆隐式规则猜(替换 shader、CameraDepthTextureMode 等),URP 把猜测改成声明。副作用是对第三方资源更可靠,代价是老 shader 缺标签时静默缺席:物体不投影、不进深度纹理,都是这个原因。
为什么 URP 强制 CBUFFER
SRP Batcher 的机制(见 /notes/unity/structure-4/)要求引擎能预知「每个材质要传哪些常量、布局是什么」。零散 uniform 没法预打包,统一进 UnityPerMaterial 才能一次打包、切材质只换指针。
HDRP 的特殊性
HDRP 更进一步:材质是**分层(Layered)**模型,手写完整光照 shader 走的是 Fabric API(Fabric.ShaderGraph.Utilities 体系),门槛高、文档少。实操建议:HDRP 里自定义材质优先 Shader Graph + Custom Node(见 /notes/unity/engineering-2/),手写整只 shader 只在做引擎级特效时考虑。
迁移步骤(Built-in → URP 实操顺序)
- 换语言壳:
CGPROGRAM→HLSLPROGRAM,include 换成 URP 库。 - 补标签:
UniversalForward+ShadowCaster+(需要时)DepthOnly/DepthNormals。 - 参数进
UnityPerMaterialCBUFFER。 - 光照函数换库:手写光照尽量改调 URP 的
UniversalFragmentPBR/LightingPhysicallyBased,把「自己算灯」变成「填参数」。 - keyword 列表换成 URP 的显式列表,用 Frame Debugger 验证每个功能 Pass 都在(见 /notes/unity/performance-2/)。
怎么选
新项目默认 URP;Built-in 只留给老项目维护;HDRP 留给明确的高端目标。三管线的 shader 知识原理层完全通用(本区域前四章),差异集中在协议层——这也是本区域把工程章放在后面的原因。
自检三问
- URP 里物体不投影,第一反应查什么标签?
- 为什么 URP 要求所有材质参数进同一个 CBUFFER?
GetMainLight()相比_WorldSpaceLightPos0多了什么能力?