1. 为什么在飞船维修场景里,PBR不是"选装项"
1.1 从Blinn-Phong到PBR:材质"统一语言"的由来
这一章不是教你怎么调出一个"好看"的材质,而是要解决一个更麻烦的问题:为什么你按照网上教程调好了金属度、光滑度,飞船维修舱里的金属板看起来还是像塑料?
先说背景。上一章我把模型导入、基础灯光和场景搭建讲完了,这一章要集中处理基于物理渲染(PBR)和Unity标准着色器(Standard Shader)的组合。这是内置渲染管线里最常用的材质方案,也是把维修场景从"灰模"推上"成品画面"的核心环节。你可能会觉得PBR就是"贴图加滑块",但真正把它用对的人很少,原因在于它背后是一套和传统Blinn-Phong完全不同的逻辑。
老一代渲染管线的思路很直白:漫反射贴图负责颜色,高光贴图负责"哪里亮",再用一个Shininess控制光斑大小。美术人员完全靠肉眼调,效果也依赖特定灯光。问题在于,换一盏灯、换一个环境,同一个材质的表现全部崩掉。维修舱里的灯源很杂——顶部荧光灯管、焊接弧光、仪表盘背光、视窗外面透进来的太空光,这些光源角度和颜色都不同。如果材质参数不是物理量,每一个光源下都要重新手工修一遍,这是不可维护的。
PBR把这个问题变成了"统一语言"。它只保留几类物理上有意义的参数:基础色(Albedo/Base Color)、金属度(Metallic)、光滑度(Smoothness),再加上法线、AO、自发光等辅助信息。渲染器基于微表面理论、能量守恒和菲涅尔反射来计算最终颜色。金属在掠射角会有强烈的反射率提升,绝缘体在任何角度都只反射大约4%的入射光,这些是物理规律,不是美术经验的产物。
所以我的第一个建议是:调材质之前,先把思维从"调颜色"切换成"描述物质"。你在滑块上写的每一个值,都是对一种真实材料的描述。金属就是金属,漆面就是漆面,橡胶就是橡胶,PBR会让它们在任意灯光组合下保持一致的表现。
1.2 金属度与高光两种工作流:维修场景该怎么选
PBR界一直有两种工作流:金属度工作流(Metallic)和高光工作流(Specular)。高光工作流用一张四通道贴图分别描述漫反射色和镜面反射色,控制更细,但资产成本和制作复杂度都高很多。金属度工作流只用一张灰度贴图区分"导体"和"绝缘体",效率和可控性更好。
Unity标准着色器默认走的是金属度工作流。原因很实际:游戏资产希望用尽量少的贴图达到稳定的物理表现。Metallic贴图中,纯黑(0)代表绝缘体,比如塑料、橡胶、木材、纸张;纯白(1)代表金属,比如钢、铝、钛;中间灰度代表"部分金属",但实战里除了锈蚀过渡带和磨损边缘,你不应该大范围使用0.5之类的灰度值——那在物理上是说不通的。
飞船维修场景里,绝大部分物体都可以明确归类:船体外壳是金属,但表面有漆层,所以基色贴图里是漆的颜色,金属度可能是0;磨损刮掉漆的位置露出裸金属,需要靠一张遮罩把金属度拉高到0.8以上;检修舱门的密封条是橡胶,金属度0;观察窗玻璃是0。如果你用单一材质覆盖整个物体,金属度只给一个固定值,那么"漆面+裸金属+锈迹"的层次就全部丢失。这就是为什么在实战中,贴图准备比滑块调节更花时间。
我的结论是:维修场景这种"大量金属与大量绝缘体共存"的环境,金属度工作流是最优解。别去钻高光工作流的牛角尖,先把Metallic贴图画好,比什么都管用。
2. 标准着色器参数逐个解剖:把滑块翻译成物理量
2.1 Albedo 和 Metallic:没有"万能灰",只有"金属依赖色"
Unity标准着色器Inspector面板上排前面的是Albedo和Metallic,很多人在这里犯第一个错:把Albedo理解为"最终颜色",然后直接填一个看起来舒服的灰。
物理上,Albedo贴图描述的是"没有受光影响时,材质反射回来并被我们看见的颜色"。对绝缘体来说,它基本等于漫反射色;但对金属来说,事情相反——纯金属的漫反射几乎为0,你看到的光全部来自镜面反射,而反射光被金属表面染色。钢是银灰色的,铜是橙红色的,金是黄色的。这个"反射染色"信息在金属度工作流里也应该放进Albedo贴图。
所以在维修舱里做一块裸露的金属板,正确做法是Albedo填一个很暗的颜色或贴图(因为金属没有漫反射),金属度给到接近1,光滑度给高一些,让它靠环境反射显示出银灰色的质感。而一块刷了灰漆的金属板,Albedo是油漆灰,金属度趋近于0,光滑度则看漆面的新旧程度。
有个常见误区是"金属度滑块只影响高光强度"。完整地说,金属度同时决定了漫反射比例、反射颜色强度、菲涅尔基础值这三件事。把一块金属的金属度从1降到0,它不只是高光变弱,还会慢慢浮现出漫反射的颜色,最终变成一个灰塑料。理解了这一点,你调参的时候就不会再怀疑"为什么金属度一降,颜色就变脏"了。
2.2 Smoothness 才是质感的分水岭
Metallic决定的是"这是不是金属",Smoothness决定的是"这块金属有多亮"。这是个粗糙度的反向指标:Smoothness=1意味着镜面抛光,=0意味着完全粗糙。物理上它对应微表面法线的分布情况——粗糙表面微观凹凸多,反射方向被打散,看起来就是磨砂的;光滑表面微观凹凸少,反射聚集成高光。
维修舱里最典型的场景是:一块船体板上,原厂漆面用了高精度喷涂,Smoothness在0.5~0.6左右,有细腻的柔光;角落里被工具砸出划痕的区域,金属被反复摩擦,表面毛糙,Smoothness掉到0.2;而门口扶手的握手位被手套常年磨过,光滑度又能回到0.8以上。同一块板子,Smoothness跨度可以很大,必须靠贴图或遮罩做进去,不能用一个滑块值糊弄。
需要注意Smoothness贴图可以放在两个通道里:Metallic贴图的Alpha通道,或者Albedo贴图的Alpha通道。标准着色器在Source下拉框里让你选。我习惯把Metallic和Smoothness合成一张纹理——R通道放金属度,G或A通道放光滑度,一是省贴图数量,二是保证两个参数空间一致,不用对两张贴图调Tiling对齐。这个小习惯能省掉大量"金属和光滑度错位"的麻烦事。
2.3 法线贴图、AO 与高度图:磨损细节从哪来
法线贴图是维修场景的"质感发动机"。铆钉周围的凹陷、焊接焊缝的鼓起、钣金件表面的轻微起伏,都不适合用几何模型去建——面数成本太高,改起来也麻烦。法线贴图把凹凸信息编码到切线空间的法线方向里,一张512贴图就能给一整面墙的细节。缝合线、划痕、冲压凹槽都是靠它表现的。
高度图(Height Map)对应的是视差映射,效果更强,但开销也更高,还会在掠射角产生明显伪影。维修舱这种近距离观察多的场景,我建议物理上需要明显深度的地方用模型做(比如检修盖的沉孔、管线护套),微细节交给法线,不要轻易开高度贴图。
AO(Occlusion)贴图在标准着色器里的位置很隐蔽,作用却至关重要:它只影响间接光照。接缝、角落、螺栓底部这些环境光打不到的地方,如果AO缺失,物体会"浮"在场景里,像被PS叠上去的。维修舱里管线密集、结构交错,AO贴图几乎决定了下层结构的立体感。等会第6节的第一个坑就是它。
2.4 Emission 与 Detail Mask:仪表盘和检修灯的处理
场景里那些自发光的元素——检修面板的屏幕、舱顶的报警灯、焊接点的高温区域——都走Emission。它的原理是让材质在光照计算之外直接输出颜色,即使是纯黑环境里也看得见。Emission贴图在制作时用HDR颜色:RGB通道写颜色,Alpha通道写强度。把强度拉超过1,配合后处理Bloom才能亮出光晕;低于1的颜色,比如暗绿色指示灯,即使开了也基本不发光。
标准着色器还提供Detail Mask和两组Secondary贴图,作用是在大面积材质上叠加一层更小的细节纹理。维修舱的金属外墙如果用一张无缝贴图铺满20米,很近的距离看一定会发现重复感。做法是在Detail Mask的R通道里画上需要细节的区域,然后给Secondary Albedo放一张高精度划痕纹理、Secondary Normal放一张细碎凹凸,等于在基础材质上面罩了一层"皮肤纹理"。这个小功能在通关很多标准着色器教学里都被忽略了,但对大面积金属来说,它比反复调光滑度更能解决"重复感"的问题。
3. 一张破损金属板的全参数实测:从资产到成片
3.1 贴图分工:Base Color、Metallic、Smoothness 各管哪一段
假设我现在要给维修舱内的主体船体板做材质。这块板子大概3米乘1米,上面有原厂漆、刮伤、裸露金属、少量表面锈蚀和灰尘。我不会只给一个材质参数,而是先做一套PBR贴图。
- Base Color(Albedo):漆面颜色为主,裸金属区域画成偏暗的钢色,锈蚀处叠加橙褐斑点。
- Metallic:漆面区域为0,裸金属和锈蚀底层为0.9~1,灰尘覆盖区域保持与底下相同的金属度(灰尘不改变"它本身是金属"这个事实)。
- Smoothness:漆面0.35~0.55,刮痕区域0.5~0.7(像被抛光过的划痕),锈蚀粗糙区域0.1~0.2。
- Normal:给铆钉边沿画一圈凹陷法线,给划痕画一条向下凹的细线。
- AO:在板边缘、螺栓周围、角落画暗角,模拟阻挡。
- Emission:只留给警示条纹和一处焊接过热区。
这里有个原则:贴图是"区域",滑块是"整体缩放系数"。你把Smoothness贴图放进槽位后,Inspector上的Smoothness滑块就变成了乘数。滑块值0.5、贴图里写0.8,实际效果是0.4。所以我一般先贴图,后调滑块,滑块只做微调,不做大改。反过来操作很容易把自己绕晕。
3.2 维修舱常用材质的基准参数表
为了方便你复制参数,我把自己项目里常用的材质基线放出来。注意这些值都基于线性色彩空间、HDR天空盒和ACES色调映射,你如果还是Gamma色彩空间,观感会不同,需要再微调。
| 材质用途 | Albedo | Metallic | Smoothness | 备注 |
|---|---|---|---|---|
| 船体原厂漆面 | 深灰蓝,RGB约(0.08,0.09,0.11) | 0 | 0.45 | 哑光工业漆 |
| 裸露钢金属 | (0.15,0.15,0.17) | 1 | 0.75 | 需要反射探针支撑 |
| 磨损划痕过渡带 | 保留底漆色 | 遮罩到0.6~0.85 | 0.3~0.6 | 遮罩制作为主 |
| 橡胶密封条 | (0.03,0.03,0.035) | 0 | 0.65 | 轻微自阴影 |
| 工具把手 | (0.1,0.05,0.02) | 0 | 0.3 | 硬橡胶 |
| 观察窗玻璃 | (0.02,0.02,0.02) | 0 | 1 | 配合透明渲染模式 |
| 警示条纹漆 | (0.05,0.12,0.05) | 0 | 0.3 | 可用Emission强化 |
这套基线不是绝对的,但比从零开始调要快很多。我每次做新场景都会先铺一遍这样的基线材质,整体效果接近后再逐块细化。采样越多,越能建立自己的视觉校准。
3.3 灯光的布置顺序:环境光 → 主光 → 反射探针
PBR材质的效果有一半取决于灯光,顺序搞反的话,金属会"等不到"反射就提前被调亮了。我的处理顺序从来是固定的,先定环境光(Skybox + Ambient),再放主光,最后补反射探针。
Skybox和Ambient决定场景的基本亮度基准。金属材质在没有反射源的纯黑环境里会变得很暗,因为它几乎不产生漫反射。这也是为什么很多人在空场景里做金属,永远调不出质感——不是参数错了,是没有东西给它反射。
主光负责塑造体积。维修舱不是太空漫射光环境,主光更适合做一个斜上方的区域吊灯来模拟舱内主照明,方向性明显,能拉出板材的明暗转折。平行光强度在HDR线性下我用1.5左右起调,方向越斜,金属表面反射越有冲击力。
最后放反射探针。维修舱里金属多,探针就是金属的"镜子"。探针放在舱室中线偏上、靠近关键视觉区的位置,烘焙时把金属物件附近的探针密度适当提高。一个常见误区:反射探针只影响光滑表面掠射角附近的反射,粗糙表面几乎看不出来。所以为了给粗糙金属补光,还是要靠环境光或额外补光,不能把探针当万能灯用。
4. 线性空间与反射探针:决定"物理感"的隐藏开关
4.1 Gamma 与 Linear 色彩空间:PBR 的底线不是风格选项
很多项目做到最后发现金属过亮、暗部发灰,核心原因不在材质面板,而在Project Settings里Color Space还挂在Gamma。
PBR的算法模型假设整个渲染管线在线性空间进行。真实世界的光强是线性的,但显示器和贴图PNG文件是按sRGB伽马曲线编码的。如果所有计算都在Gamma空间做,等于把"显示器的非线性"也混进了光照计算里,结果就是暗部被提亮、高光压缩、金属的菲涅尔过渡变得生硬,怎么调都不对劲。
标准着色器在Gamma下其实也做了部分校正,但那是将就,不是正确。把Color Space切到Linear后,Albedo贴图必须在导入设置里保持sRGB勾选(Unity会自动把sRGB解码成线性数值再送去计算),而Metallic、Smoothness、Normal、AO这些数据贴图必须取消sRGB勾选,因为它们不是颜色,是线性浮点数据。这一条在第6节会有更详细的坑,这里先记住结论。
4.2 反射探针的密度与 Box Projection 设置
反射探针是内置渲染管线下金属质感的核心来源。Skybox能给大方向的环境反射,但要表现金属板之间互相反射、管道镀层映出旁边机柜颜色,就得靠探针。
探针不是越多越好。每新增一个Realtime探针都会多一份实时渲染开销,我常用的组合是"1个场景包围盒探针 + 3~4个局部探针",并全部设为Baked。局部探针放在维修台的顶部、船体板转角、舱门观察窗附近,每一颗都用Box Projection开启边界盒约束。维修舱是个封闭空间,墙壁、管线、地板构成明确的盒形约束,Box Projection能极大提升反射方向感,避免金属反射看起来像是"外太空"的横切面。
探针还有个细节:它在材质面板里对应一个Reflection滑块,默认1,表示反射强度权重。玻璃和抛光金属可以保持1,但车间的哑光地板会反射得很乱,我会把地板材质的Reflection降到0.15~0.3,既保留少量环境反射,又不至于反射出一堆细节分散注意力。
4.3 后处理里的 Bloom 与色调映射:金属高光的最后一道闸门
到了后处理这步,金属质感基本成型,剩下就是色调映射和Bloom的博弈。维修场景里焊接点、报警灯、高光抛光的金属板都是高亮区域,如果Bloom阈值设得太低,整个画面会糊成一片白光。
我用后处理栈时,Bloom阈值一般设在1.1~1.3之间。低于这个值的像素不会产生泛光,焊接弧光的Emissive强度拉到3~5时才能出现柔和扩散。色调映射选ACES,它对高光的压缩比线性更温和,金属高光不容易直接被切到纯白,层次保留得更好。调完色调映射后再回头复查材质:如果金属板高光区域依然发"死白",问题大概率在Smoothness太高或主光强度过大,而不是后处理不够强。
5. 性能底线:标准着色器在复杂场景里的可控成本
5.1 合批、实例化与 Shader 变体:先搞清楚钱花在哪
标准着色器功能全,代价是Shader变体多。每当你勾选一个功能(Emission、Normal Map、Detail Mask),Unity就会在构建时生成一个新的Keyword组合,形成单独的变体。材质A开了Emission、材质B没开,就算其他参数一样,它们也属于两个不同Pass,没法合批。
在维修舱这种大资产数量场景里,我第一动作是统计材质数量。一个500面管道的模型,如果每根管道单独分一个材质,Draw Call直接爆炸。正确做法是同类物体共享材质,靠贴图的UV变化产生差异,而不是复制材质改参数。
具体开销表现可以参考这张表:
| 优化项 | 作用 | 维修场景里的建议 |
|---|---|---|
| 静态合批 | 把静态物体合并为批次 | 船体结构件全部勾选Static |
| GPU Instancing | 复用同网格同材质 | 螺栓、支架、线缆卡扣等大量同类物 |
| Shader变体裁剪 | 减少构建体积和加载内存 | 关闭不需要的Keyword |
| 减少Realtime灯光 | 避免逐像素光照开销 | 主光之外全用烘焙或探针 |
| 减少材质数量 | 降低切换材质状态的开销 | 管线用同一套贴图换Tiling |
5.2 大面积金属的替代做法:何时不该继续用 Standard Shader
标准着色器是内置渲染管线的默认王者,但真到了优化阶段,它并不总是最优解。遇到两种场景我会换掉它:一是整面巨大的金属墙,省掉漫反射计算也无所谓,因为金属本来漫反射就少;二是远处的次级资产,肉眼看不清材质细节,完全可以用简化的Mobile Shader或自制Shader代替。
大面积金属我更推荐用自制Surface Shader或直接写vert/frag:一个主方向光的反射、加上一张金属反射贴图和探针,效果就够了。标准着色器的Smoothness细节、微表面计算在这种尺度下全是浪费。在维修舱这种强金属属性场景里,这类替代方案能把主要物体的Shader开销砍掉一半以上。
5.3 移动端与低端机的退路
如果你要把维修场景跑在移动端,内置渲染管线加标准着色器的压力会立刻显现。不是不能用,但要注意:关闭实时反射探针的刷新,把探针全部烘焙;灯光数量压到1盏主光+1盏补光;法线贴图降采样到512以下;Bloom和SSAO这类后处理特效慎开或者用低档版本。
更稳妥的路是切到URP。URP下的Lit Shader逻辑和标准着色器同源,但移动端优化做得好得多,SRP Batcher还能大幅减少Draw Call。维修场景的素材本来就是PBR资产,迁移成本并不高。如果项目从第一天就考虑移动端,我建议直接URP起步,别在Built-in管线里做完一半才回炉。
6. 踩坑实录:我在维修场景里犯过的四个错误
6.1 场景发灰,不是灯光问题,是 AO 图连错了
有一版维修舱画面,所有板材的转角都"发闷",像蒙了一层雾,我把Skybox调亮、主光加倍,还是灰。排查了两小时,最后发现是AO槽位里放了一张灰色噪点很大的图。AO只影响间接照明,正确的AO图应该反映物体的"自遮挡"关系——缝隙深黑、开阔面亮白。我那张图是从BaseColor里抽出亮度做的,等于给间接光盖了一层灰纱,画面一定脏。
现在我把AO图单独输出成线性灰度图,导入时关掉sRGB,再在材质面板里确认它是挂在Occlusion槽而不是Albedo槽。检查方法很简单:把Metallic、Smoothness全部临时归零,如果场景层次感立刻消失,就是AO没接对。
6.2 金属高光爆掉:Smoothness、灯光强度与 HDR 的联动
有段时间我把地板的Smoothness统一设成0.9,舱顶主灯又开到3的强度,结果金属地面反射出一个刺眼的“灯棍”,整块板的高光区域一片死白。PBR是乘法关系,高光滑表面上一个小范围光源的能量会聚集成一个强反射斑,数值上很容易溢出。
现在我的校准流程是:先让Smoothness保持在0.5以下观察整体,确认无过曝后再逐步抬升;主灯强度控制在1.2~2.0之间;碰到实在够亮的反射,在材质上把Reflection权重降到0.4~0.6。记住一点,画面里必须有暗部,金属才会有“闪”起来的空间,全部提亮只会得到一张平庸的白墙。
6.3 法线贴图上下颠倒:一个像素通道引发的"凹凸变凹陷"
我做焊接焊缝法线时犯过最诡异的错:焊缝在模型上明明是凸起的,渲染出来却像一条凹槽。查了半圈,问题出在法线贴图的绿色通道方向。Unity的切线空间法线贴图遵循类似OpenGL的约定:模型坐标Y轴向下的环境下,G通道值越高法线越朝上,而我从某个DDS文件直接转过来的法线贴图是DirectX风格,Y方向相反。
解决方式:在纹理导入后用贴图工具(如PhotoShop或Substance的导出选项)把绿色通道翻转,或者干脆在导出法线贴图时选择“Flip Green”的选项。以后接手任何第三方法线贴图,我都会先在Unity里用一条明显的凸起焊缝做验证,再批量套用,省得最后一遍遍返工。
6.4 sRGB 勾选错误:让 Metallic 贴图变成"曝光过度"
这是新手最容易踩、也最难发现的隐形坑。Metallic贴图本质是线性灰度数据,值0代表绝缘体、1代表金属。如果导入时忘了取消sRGB勾选,Unity会把这张贴图里的1.0当2.2用:0.8的金属度会被提亮成0.9还多,整个金属和非金属的分界会全部错位。表现就是金属区域泛白、绝缘体区域莫名发光,怎么调金属度滑块都没用,因为贴图本身已经在错误空间里了。
我现在的导入规则固定如下:Albedo和Emission贴图保留sRGB勾选;Metallic、Smoothness、Normal、AO、Detail Mask全部取消sRGB。这个规则写成了团队规范,之后再也没有出现过这类"玄学"问题。每次拿到外包贴图,我的第一件事不是看效果,而是打开Texture Import面板把所有数据贴图的sRGB勾选检查一遍。
这套流程跑完整整实验下来,我最大的体会是:PBR不是一个"调滑块能调出好效果"的功能,而是一套需要你约束自己的规范。约束贴图空间、约束色彩空间、约束探针密度,最后约束你自己不要随手乱拖参数,出来的画面自然就有了"物理感"。如果你现在正卡在某个金属材质调不出质感,回到我最开始说的那句话——先想清楚你在描述什么物质,再去碰滑块。