Built-in / URP / HDRP:写法差异与差异的原因

Unity 知识 · Shader 区域 · 管线与工程 · 第 1 篇 更新于 2026-10-15

同一份「手写光照」在 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-inURP
代码块CGPROGRAMHLSLPROGRAM(CG 语法不支持)
基础库UnityCG.cginc / Lighting.cgincUniversal/Core.hlsl / Lighting.hlsl / CommonMaterial.hlsl
采样tex2D(_T, uv)SAMPLE_TEXTURE2D(_T, sampler_T, uv)(需 TEXTURE2D+SAMPLER 声明)
变换UnityObjectToClipPosTransformObjectToHClip(后缀 OS/WS/VS/CS 命名约定)
主光_WorldSpaceLightPos0、_LightColor0GetMainLight() 返回 Light 结构
Pass 挑选LightMode 部分隐式(ForwardBase 等)SRP 按 LightMode 标签挑 Pass:UniversalForward、ShadowCaster、DepthOnly、DepthNormals、SceneSelection… 标签错 = 该功能缺这个物体
材质参数零散 uniform 即可全部进 CBUFFER_START(UnityPerMaterial),否则 SRP Batcher 不兼容(见 /notes/unity/structure-4/)
阴影 keywordmulti_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 实操顺序)

  1. 换语言壳:CGPROGRAM→HLSLPROGRAM,include 换成 URP 库。
  2. 补标签:UniversalForward + ShadowCaster +(需要时)DepthOnly/DepthNormals。
  3. 参数进 UnityPerMaterial CBUFFER。
  4. 光照函数换库:手写光照尽量改调 URP 的 UniversalFragmentPBR/LightingPhysicallyBased,把「自己算灯」变成「填参数」。
  5. keyword 列表换成 URP 的显式列表,用 Frame Debugger 验证每个功能 Pass 都在(见 /notes/unity/performance-2/)。

怎么选

新项目默认 URP;Built-in 只留给老项目维护;HDRP 留给明确的高端目标。三管线的 shader 知识原理层完全通用(本区域前四章),差异集中在协议层——这也是本区域把工程章放在后面的原因。

自检三问

  • URP 里物体不投影,第一反应查什么标签?
  • 为什么 URP 要求所有材质参数进同一个 CBUFFER?
  • GetMainLight() 相比 _WorldSpaceLightPos0 多了什么能力?