1. PBR 与 URP 管线的基础认知重构
1.1 为什么选择 URP 管线做 PBR 学习
很多人入门 PBR 的第一反应是直接上 HDRP,觉得效果越华丽越好。我一开始也是这个思路,结果在 HDRP 里折腾了两周,连一个正确的金属球都没调出来。后来退回到 URP 重新梳理,才发现问题不在管线本身,而在于我对 PBR 的基础认知是断裂的。
URP 管线做 PBR 学习有几个非常实际的优势。第一,它的 Shader 代码结构清晰,Lit Shader 的源码量适中,不像 HDRP 那样被大量的体积雾、光线追踪、次表面散射等高级特性包裹,导致你很难看清 BRDF 的核心计算流程。第二,URP 的渲染路径相对简单,Forward 和 Deferred 的切换逻辑明确,方便你对照理解不同路径下 PBR 计算的差异。第三,URP 的跨平台兼容性好,你在编辑器里调好的材质参数,打包到移动端后偏差相对可控,这对建立“参数直觉”非常重要。
我个人的建议是:如果你刚接触 PBR,先把 URP 的 Lit Shader 吃透,再去碰 HDRP 或自定义 SRP。这就像学做菜,先把一道番茄炒蛋做到火候精准,再去研究分子料理。
1.2 PBR 核心参数到底在控制什么
PBR 的全称是 Physically Based Rendering,基于物理的渲染。这句话的关键词不是“渲染”,而是“物理”。它意味着材质的每一个参数都对应着现实世界中的某种物理量,而不是美术凭感觉随便拉的滑块。
在 URP 的 Lit Shader 里,最核心的参数就那几个:Base Map(基础贴图)、Metallic(金属度)、Smoothness(光滑度)、Normal Map(法线贴图)、Height Map(高度图)、Occlusion(环境光遮蔽)。很多人会把 Metallic 和 Smoothness 混在一起理解,觉得“金属就是亮,光滑就是反光”,这其实是不对的。
Metallic 控制的是材质的“导电性”。金属度接近 1 的材质,漫反射几乎为零,所有入射光要么被镜面反射,要么被吸收。金属度接近 0 的材质(比如塑料、木头、皮肤),漫反射占主导,镜面反射很弱。Smoothness 控制的是微观表面的平整程度,它直接决定高光斑的锐利程度和反射的清晰度。一个粗糙的金属(Metallic=1, Smoothness=0.2)看起来是哑光的,一个光滑的塑料(Metallic=0, Smoothness=0.9)看起来是亮面但反射模糊。
这里有一个我踩过的坑:很多人会把 Smoothness 和 Roughness 搞混。URP 用的是 Smoothness,值越大越光滑;而有些引擎或教程用的是 Roughness,值越大越粗糙。两者是反关系,即 Roughness = 1 - Smoothness。你在看教程时一定要先确认对方用的是哪个。
1.3 BRDF 在 URP 中的实际计算流程
BRDF 是 Bidirectional Reflectance Distribution Function 的缩写,双向反射分布函数。这个名字听起来很学术,但它的本质就是回答一个问题:一束光打到表面上,有多少能量被反射到观察者方向?
在 URP 的 Lit Shader 中,BRDF 的计算被拆分成两部分:漫反射项(Diffuse)和镜面反射项(Specular)。漫反射项用的是 Lambert 模型,简单说就是光线方向与法线方向的点积,再除以圆周率 π 做归一化。镜面反射项用的是 GGX 分布,这是目前实时渲染中最主流的微表面模型。
GGX 的核心思想是:把表面看成由无数微小的镜面组成,每个微镜面的朝向服从某种统计分布。Smoothness 越高,微镜面朝向越集中,高光越锐利;Smoothness 越低,微镜面朝向越分散,高光越模糊。这个分布函数叫 D 项,再加上几何遮蔽项 G 和菲涅尔项 F,合起来就是著名的DFG 模型。
URP 的 Lit Shader 里,这些计算被封装在Lighting.hlsl和BRDF.hlsl两个文件中。你可以在 Package Manager 里找到 Universal RP 包,然后定位到Shaders/LitForwardPass.hlsl,顺着UniversalFragmentPBR函数一路跟下去,就能看到完整的 BRDF 计算链路。我强烈建议你至少手动跟一遍这个流程,比看十篇理论文章都管用。
2. URP 中 PBR 材质制作的核心细节与实操要点
2.1 金属度与光滑度的参数搭配逻辑
在 URP 的 Lit Shader 里,Metallic 和 Smoothness 是分开的两个滑块,但它们在物理上是有关联的。现实世界中,纯金属的 Smoothness 通常较高(因为金属表面容易抛光),而氧化或粗糙的金属 Smoothness 较低。非金属材料的 Smoothness 范围很广,从橡胶的 0.1 到玻璃的 0.95 都有。
我整理了一份常用材质的参数参考表,这些值是我在实际项目中反复调试后总结的,可以直接拿来用:
| 材质类型 | Metallic | Smoothness | 备注 |
|---|---|---|---|
| 纯金 | 1.0 | 0.85-0.95 | 颜色贴图用金色,不要用默认白 |
| 纯银 | 1.0 | 0.90-0.95 | 颜色贴图用浅灰白 |
| 铝 | 1.0 | 0.80-0.90 | 比金和银稍粗糙 |
| 铁 | 1.0 | 0.70-0.85 | 未抛光时更低 |
| 塑料 | 0.0 | 0.60-0.90 | 看表面处理,哑光塑料 0.3-0.5 |
| 橡胶 | 0.0 | 0.10-0.30 | 几乎无高光 |
| 木材 | 0.0 | 0.20-0.50 | 清漆处理后可到 0.7 |
| 皮肤 | 0.0 | 0.30-0.50 | 需要配合次表面散射 |
| 玻璃 | 0.0 | 0.90-0.98 | 需要透明模式 |
| 陶瓷 | 0.0 | 0.70-0.90 | 釉面陶瓷更高 |
注意:这张表里的 Smoothness 是 URP 的取值,如果你用的是 Roughness 工作流,记得做 1-x 转换。
这里有一个非常关键的实操心得:金属材质的颜色贴图必须是有颜色的,而非金属材质的颜色贴图应该是去饱和的。很多新手会把金属的 Base Map 做成灰色,然后指望 Metallic 滑块来出金属感,结果渲染出来像一块灰铁。正确的做法是,金属的 Base Map 直接给金色、银色、铜色等,Metallic 拉到 1,Smoothness 根据粗糙程度调整。非金属的 Base Map 给实际颜色,Metallic 保持 0。
2.2 法线贴图与高度贴图的正确使用姿势
法线贴图在 URP 里有一个容易忽略的细节:法线贴图的导入设置必须正确。在 Unity 的 Texture Import Settings 里,Texture Type 要选 Normal Map,Create from Grayscale 不要勾(除非你确实是从灰度图生成法线)。如果导入设置错了,法线方向会完全乱掉,表面看起来像被扭曲了一样。
法线贴图的强度由 Normal Map 旁边的滑块控制,默认是 1。这个值不是越大越好。我见过很多项目把法线强度拉到 2 甚至 3,结果表面细节看起来像刀刻的一样生硬。一般来说,0.8 到 1.2 之间是比较自然的范围。如果你觉得细节不够,应该去改法线贴图本身,而不是无脑拉强度。
高度贴图在 URP 的 Lit Shader 里叫 Height Map,它需要配合 Parallax 参数使用。Parallax 是一种视差效果,让表面看起来有真实的凹凸深度。但这里有一个性能陷阱:Parallax 在移动端非常昂贵,因为它需要在像素着色器里做多次纹理采样。如果你的项目要上移动端,建议用 Normal Map 代替 Height Map,或者把 Parallax 的采样次数降到最低。
我个人的经验是,对于大多数场景,法线贴图已经足够表达细节了。高度贴图只在特写镜头或需要强烈立体感的物体上才值得用。比如一面砖墙,法线贴图能表现砖缝的凹凸,但如果你想要砖块边缘有真实的遮挡感,那就需要高度贴图加 Parallax。
2.3 环境光遮蔽与反射探针的配合
Occlusion Map 在 URP 里叫 Occlusion,它控制的是环境光在缝隙处的衰减。很多人会把 Occlusion 和阴影搞混,其实它们完全不同。阴影是直接光被遮挡,Occlusion 是环境光被遮挡。一个物体在阴影里,但它的缝隙处仍然可能接收到环境光,Occlusion 就是用来压暗这些缝隙的。
在 URP 里,Occlusion 的强度由 Occlusion Strength 滑块控制,默认是 1。如果你发现模型缝隙处太黑,可以适当降低这个值。但要注意,Occlusion 贴图应该是烘焙出来的,而不是手绘的。手绘的 Occlusion 往往不符合物理规律,会导致光照看起来不自然。
反射探针是 URP 里做 PBR 反射的关键组件。没有反射探针,金属材质只能反射天空盒,看起来会很假。反射探针的放置有几个原则:第一,探针要放在场景中反射影响最明显的区域;第二,探针的 Type 要根据场景大小选择,Baked 适合静态场景,Realtime 适合动态物体但性能开销大;第三,探针的 Resolution 不要盲目拉满,128 或 256 在大多数场景下已经足够。
提示:在 URP 里,反射探针的强度可以通过 Lit Shader 的 Specular Highlights 和 Environment Reflections 两个开关分别控制。如果你想要金属反射更明显,确保 Environment Reflections 是开启的。
3. 从零搭建一个 URP PBR 测试场景的完整流程
3.1 项目初始化与 URP 配置
第一步,创建一个新的 Unity 项目,模板选择 3D (URP)。如果你用的是 Unity Hub,在创建项目时直接选 URP 模板即可。如果你是在已有项目里切换管线,需要先安装 Universal RP 包,然后创建 URP Asset,再在 Project Settings 的 Graphics 里指定这个 Asset。
URP Asset 的配置有几个关键点。Lighting 部分,Main Light 的 Shadows 要开启,Additional Lights 的 Per Object Limit 根据场景灯光数量调整,一般 4 到 8 够用。Quality 部分,Anti Aliasing 建议选 MSAA 4x,HDR 要开启,这样高光才不会过曝。Shadows 部分,Shadow Distance 不要设太大,50 到 100 米足够,太大反而会让阴影贴图精度下降。
这里有一个我踩过的坑:URP 的 Render Scale 默认是 1.0,但如果你在移动端做测试,可以降到 0.8 或 0.9 来提升性能。不过要注意,Render Scale 降低后,法线贴图的细节会丢失,因为法线贴图对分辨率很敏感。
3.2 创建测试材质与场景布置
在 Project 窗口右键,Create > Material,然后在 Inspector 里把 Shader 改成 Universal Render Pipeline/Lit。这就是 URP 的标准 PBR Shader。
场景布置我建议用一个经典的“PBR 测试球阵”:一排球体,每个球体用不同的 Metallic 和 Smoothness 组合。比如 Metallic 从 0 到 1 分五档,Smoothness 从 0 到 1 分五档,一共 25 个球。这样你可以直观地看到参数变化对最终效果的影响。
灯光方面,用一个 Directional Light 做主光,强度 1 到 2,角度调成 45 度左右。再加一个 Point Light 做补光,强度 0.5 到 1,放在球的侧后方。环境光用 Gradient 或 Skybox,Intensity 控制在 0.5 到 1 之间。如果你想要更真实的反射,可以在场景里放一个 Reflection Probe,Bake 一下。
注意:测试场景的背景不要用纯黑,纯黑会让金属反射看起来像黑洞。用深灰色或渐变天空盒,这样金属表面能反射出环境细节。
3.3 逐参数调试与效果对比
调试 PBR 材质时,我习惯按固定顺序来:先调 Base Map,再调 Metallic,然后调 Smoothness,最后加法线贴图。这个顺序很重要,因为前面的参数会直接影响后面参数的视觉效果。
Base Map 阶段:先给一个纯色,确认颜色在光照下的表现。如果是金属,给金色或银色;如果是非金属,给实际颜色。这时候 Metallic 和 Smoothness 都先保持默认(Metallic=0, Smoothness=0.5)。
Metallic 阶段:把 Metallic 从 0 慢慢拉到 1,观察漫反射的消失和镜面反射的增强。你会看到,Metallic 到 0.5 左右时,材质看起来既不像金属也不像非金属,这就是所谓的“半金属”,在物理上是不存在的。所以实际项目中,Metallic 要么是 0,要么是 1,中间值只用于过渡区域。
Smoothness 阶段:把 Smoothness 从 0 拉到 1,观察高光斑的变化。Smoothness 低时,高光斑大而模糊;Smoothness 高时,高光斑小而锐利。对于金属,Smoothness 还会影响反射的清晰度。你可以放一个 Reflection Probe,然后旋转视角,看反射内容的变化。
法线贴图阶段:最后加法线贴图,强度从 0.5 开始慢慢加。法线贴图会改变表面的微观朝向,从而影响高光的形状。如果法线贴图太强,高光会碎成很多小块,看起来很不自然。
3.4 性能分析与优化建议
URP 的 PBR 性能开销主要来自三个方面:像素着色器的 BRDF 计算、纹理采样、阴影和反射探针。
BRDF 计算在 URP 里已经做了不少优化,比如用近似公式代替精确计算。但如果你在移动端,还是要注意 Additional Lights 的数量。每多一盏 Additional Light,像素着色器就要多做一次 BRDF 计算。我建议移动端 Additional Lights 的 Per Object Limit 设为 2 到 4。
纹理采样方面,Lit Shader 最多会采样 Base Map、Normal Map、Metallic/Smoothness Map、Occlusion Map、Height Map 五张纹理。如果你用了 Height Map 加 Parallax,采样次数会翻倍。移动端建议把 Metallic 和 Smoothness 打包到同一张纹理的 R 和 A 通道,Occlusion 打包到 G 通道,这样可以减少纹理采样次数。
阴影和反射探针是性能大户。Shadow Distance 每增加一倍,阴影贴图的渲染开销就增加一倍。Reflection Probe 如果是 Realtime 的,每帧都要渲染六面 cubemap,开销非常大。我建议静态场景用 Baked 探针,动态物体用 Realtime 但降低 Resolution 和 Update Frequency。
4. 常见问题排查与避坑经验实录
4.1 材质发黑或过曝的排查思路
材质发黑是最常见的问题,原因通常有这几个:法线贴图导入设置错误、Metallic 和 Smoothness 不匹配、环境光强度太低、反射探针缺失。
排查顺序我建议这样:先检查法线贴图的 Texture Type 是不是 Normal Map,如果不是,改过来重新导入。然后检查 Metallic 是不是 1 但 Base Map 是灰色,如果是,把 Base Map 改成有颜色的。接着检查环境光,在 Lighting 窗口里看 Environment Lighting 的 Intensity 是不是太低。最后检查反射探针,如果场景里没有探针,金属只能反射天空盒,看起来会很暗。
材质过曝通常是 Smoothness 太高加上 HDR 没开导致的。Smoothness 到 1 时,高光会非常集中,如果 HDR 没开,高光会直接截断成白色。解决办法是开启 URP Asset 里的 HDR,或者把 Smoothness 降到 0.9 以下。
4.2 金属反射不清晰的解决方法
金属反射不清晰,通常是因为反射探针的 Resolution 太低,或者探针的位置不对。Reflection Probe 的 Resolution 默认是 128,对于大场景来说太低了。你可以提到 256 或 512,但要注意性能开销。
探针的位置也很关键。探针应该放在场景中反射影响最明显的区域,比如金属物体附近。如果探针离金属太远,反射内容会不准确。另外,探针的 Box Size 要覆盖金属物体的范围,否则反射会错位。
还有一个容易被忽略的点:URP 的 Lit Shader 里有一个 Environment Reflections 的开关,在 Specular Highlights 下面。如果这个开关没开,金属不会反射环境探针,只会反射天空盒。很多人找不到这个开关,以为反射探针坏了,其实就是没开。
4.3 移动端 PBR 的性能陷阱
移动端做 PBR 有几个硬性限制。第一,纹理采样次数,移动端 GPU 的纹理采样带宽有限,五张纹理全采样会非常吃力。建议把 Metallic、Smoothness、Occlusion 打包到一张纹理,Base Map 和 Normal Map 保留,Height Map 去掉。
第二,Additional Lights 数量,移动端建议不超过 4 盏。如果场景需要更多灯光,考虑用 Lightmap 烘焙静态光,动态光只保留主光。
第三,阴影质量,移动端的阴影贴图分辨率不要超过 1024,Shadow Distance 不要超过 30 米。软阴影在移动端也很昂贵,建议用硬阴影或低质量的软阴影。
第四,反射探针,移动端尽量用 Baked 探针,Realtime 探针只在必要时用,并且 Resolution 降到 64 或 128。
我实测下来,一个中等复杂度的移动端场景,如果严格按照上述限制来配置,URP 的 PBR 可以在中端手机上跑到 30 帧以上。但如果放开限制,帧率会直接掉到 15 帧以下。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 材质发黑 | 法线贴图导入错误 | 检查 Texture Type 是否为 Normal Map |
| 材质发黑 | Metallic=1 但 Base Map 为灰色 | 给 Base Map 上金属色 |
| 材质发黑 | 环境光强度太低 | 提高 Environment Lighting Intensity |
| 材质过曝 | Smoothness 太高且 HDR 未开 | 开启 HDR 或降低 Smoothness |
| 金属反射模糊 | 反射探针 Resolution 太低 | 提高到 256 或 512 |
| 金属无反射 | Environment Reflections 未开 | 在 Lit Shader 里开启该开关 |
| 法线效果生硬 | 法线强度过高 | 降到 0.8-1.2 范围 |
| 移动端帧率低 | 纹理采样过多 | 打包 Metallic/Smoothness/Occlusion |
| 移动端帧率低 | Additional Lights 过多 | 降到 4 盏以下 |
| 移动端帧率低 | Realtime 反射探针 | 改用 Baked 探针 |
提示:这张表里的问题我几乎都遇到过,尤其是“金属无反射”这一条,当时排查了两个小时才发现是 Environment Reflections 没开。建议你遇到问题时先对照这张表过一遍,能省不少时间。
4.5 一些不那么常见但很关键的细节
关于 Gamma 和 Linear 空间:URP 默认使用 Linear 颜色空间,这是 PBR 正确工作的前提。如果你在 Player Settings 里把颜色空间改成了 Gamma,PBR 的光照计算会完全错误,材质看起来会发灰。所以,永远不要在 URP 项目里用 Gamma 空间。
关于纹理的 sRGB 设置:Base Map 和 Occlusion Map 应该是 sRGB 的,因为它们是颜色信息。Normal Map、Metallic Map、Smoothness Map、Height Map 应该是 Linear 的,因为它们不是颜色信息。如果搞反了,法线方向会错,金属度会偏。
关于 Shader Graph 和手写 Shader 的选择:如果你只是做标准 PBR 材质,用 Lit Shader 就够了。如果你需要自定义效果,比如双面材质、卡通渲染、特殊的高光形状,可以用 Shader Graph 或手写 HLSL。但要注意,自定义 Shader 很容易破坏 PBR 的物理正确性,除非你很清楚自己在做什么。
关于双面材质:URP 的 Lit Shader 默认是单面的。如果你需要双面材质,比如树叶、布料、纸张,可以在 Shader Graph 里把 Render Face 设为 Both。但双面材质会有法线翻转的问题,背面法线需要手动翻转,否则光照会错误。
关于视图渲染和渲染队列:URP 的渲染队列控制着物体的绘制顺序。Opaque 物体默认在 Geometry 队列(2000),Transparent 物体在 Transparent 队列(3000)。如果你发现透明物体遮挡了不透明物体,或者渲染顺序不对,检查一下材质的 Render Queue 设置。
我在实际项目中还遇到过一个坑:URP 的 Lit Shader 在移动端不支持 Parallax 的某些高级模式。如果你在编辑器里调好了 Parallax 效果,打包到移动端后发现效果不对,大概率是因为移动端不支持某些 Parallax 变体。解决办法是改用 Normal Map 加简单的视差偏移,或者直接在移动端关掉 Parallax。
最后分享一个小技巧:如果你想要快速验证一个 PBR 材质的物理正确性,可以把它放在一个纯白环境里,用一盏标准强度的平行光照射。物理正确的材质,在纯白环境下应该看起来和现实中的同类材质非常接近。如果看起来像塑料但你想做金属,或者看起来像金属但你想做塑料,那一定是 Metallic 或 Smoothness 设错了。这个方法我用了很多次,比任何理论推导都直观。