本节摘要:PBR(基于物理的渲染)用金属度与粗糙度两个参数描述材质本质,用环境贴图提供真实的光照来源,三者在不同灯光下保持一致的观感。本节讲清 PBR 的参数体系、环境贴图为什么是必需品而非装饰品,并给出"PBR 场景发灰"的诊断流程。承接 3.1 的通道概念,通向 3.3 的纹理采样。
读完后你应当能够:
3.1 节的 StandardMaterial 调高光,本质是"猜":specularPower 等于 32 意味着什么物理事实?没有答案,它只是个让观感对的旋钮。PBR 的立场不同:表面属性对应真实物理量——金属度回答"这是不是导电体",粗糙度回答"表面微观上有多少凹凸"。参数有了物理含义,收益随之而来:同一套参数在正午阳光与黄昏室内保持一致的质感,美术与程序之间有了共同语言,材质可以从一个引擎搬到另一个引擎。
两个核心参数的观感曲线值得亲手扫一遍:

代码里两个参数对应两个属性,环境贴图通过一个专用纹理通道提供:
// PBR 最小配置:两参数加环境贴图 const pbr = new BABYLON.PBRMaterial("pbr", scene); pbr.albedoColor = new BABYLON.Color3(0.9, 0.75, 0.35); // 金的黄铜底色 pbr.metallic = 1.0; // 金属度:1 导体,0 绝缘体,中间态少用 pbr.roughness = 0.25; // 粗糙度:0 镜面到 1 哑光 pbr.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("environment.dds", scene); ball.material = pbr;
PBR 材质对环境贴图的依赖常被低估。没有它,材质就像通了电没开闸:粗糙表面靠"环境漫反射"补底光,金属表面靠"环境镜面反射"呈现质感,缺了环境数据,漫反射失真、镜面反射干脆没有,整球发灰发暗。诊断"PBR 发灰"的顺序固定为三步:先查环境贴图是否存在,再查它的强度(environmentIntensity),最后查灯光是否重复补光造成过曝。
环境贴图的来源有两种。预过滤的专用格式体积小、效果稳定,适合线上交付;引擎还能从当前场景实时生成环境,代价是每帧多一遍渲染,适合环境本身在变化的场景(车内看天空)。两条路按场景动静来选:
// 路线一:预过滤环境贴图,静态场景的标准做法 scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("env.dds", scene); scene.environmentIntensity = 0.9; // 全局环境亮度旋钮 // 路线二:从场景实时生成,环境本身在动时才用 const mirror = new BABYLON.MirrorTexture("live", 512, scene, true); mirror.renderList = [skybox, ground]; // 指定哪些物体进入环境 mirror.refreshRate = BABYLON.RenderTargetTexture.REFRESHRATE_RENDER_ONEVERYFRAME; // 每帧更新,注意帧预算
⚠️ 常见坑:环境贴图与灯光双重补光。环境已经提供整体亮度,再按 StandardMaterial 的习惯打满三盏灯,画面整体过曝。PBR 场景的布光习惯要反过来:环境打底,灯光只做主光与轮廓。
把参数体系走成完整流程。背景:一只金属表壳加皮革表带的手表要在网页里 360 度展示,设计师给了照片参考,直射光打出来的金属像塑料。操作分三步。第一步换 PBR:表壳金属度 1、粗糙度 0.18;表带金属度 0、粗糙度 0.65、底色深棕。第二步配环境:选室内摄影棚风格的预过滤环境图,环境强度从 1.0 起调;主光只留一盏方向光压出表侧轮廓,强度明显低于环境贡献。第三步微调:反光刺眼就降环境强度而不是删灯,暗部发死就把环境强度提到 1.2 再压主光。结果:金属表面出现清晰的明暗渐变,转动时高光顺着表圈流动,与参考照观感接近。解读:金属的观感几乎全部来自环境图,灯光只负责雕刻轮廓——这也是"换了 PBR 反而更丑"的抱怨多半出在没有环境图的原因。变式:户外场景把比例反过来,太阳光当主光、天空环境做底光,参数不变只动强度分配。
// 手表展示的完整光照链路:环境打底,主光雕刻轮廓 scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("studio.env", scene); scene.environmentIntensity = 1.0; // 环境贡献作为基准亮度 const key = new BABYLON.DirectionalLight("key", new BABYLON.Vector3(-0.4, -0.8, -0.3), scene); key.intensity = 0.35; // 主光低于环境:只压轮廓不当主照明 const caseMat = new BABYLON.PBRMaterial("case", scene); caseMat.albedoColor = new BABYLON.Color3(0.83, 0.82, 0.8); caseMat.metallic = 1.0; // 表壳:导体 caseMat.roughness = 0.18; // 抛光金属的低粗糙度 const strapMat = new BABYLON.PBRMaterial("strap", scene); strapMat.albedoColor = new BABYLON.Color3(0.16, 0.1, 0.07); strapMat.metallic = 0.0; // 表带:绝缘体 strapMat.roughness = 0.65; // 皮革的哑光手感
PBR 和 StandardMaterial 怎么选? 新项目默认 PBR:参数物理含义明确、可跨场景复用,与建模软件输出的金属度粗糙度贴图直接对得上。StandardMaterial 留给两类场合——低配设备上更便宜的非金属场景,以及只要卡通感、不想管理环境图的原型验证。
环境强度调到多少合适? 没有万能值,但有校准顺序:先把环境强度归一,用主光加对比,最后才动环境强度做氛围微调。颠倒顺序会在两个旋钮之间来回打转,越调越糊。
金属度能用 0.5 吗? 能渲染,但物理上几乎没有半金属。金属度当开关用(0 或 1),过渡质感交给粗糙度与底色,这是材质师的主流习惯,也让参数扫参时观感可预测。
两个参数与一张环境图构成了 PBR 的骨架,细节层面的凹凸、贴图与自定义着色能力在下一节展开——那也是美术与程序员在材质上真正分工的地方。