本章实测环境Babylon.js9.30.0、Vite 8.3.4、TypeScript 5.9.3、Windows 11 ChromeWebGL2 后端。本章代码src/chapters/ch13-material-comparison.ts运行后打开http://localhost:18000/?ch13。图中所有源码出处都指node_modules/babylonjs/core里的真实文件与行号你可以自己打开核对。§13.1 本章要做成什么样最终效果一个材质对比实验室并排展示三个相同的球体分别使用 StandardMaterial、PBRMaterial 和 OpenPBRMaterial 渲染。场景中有四个可调节的光源环境光 三个方向光读者可以拖动滑块实时改变材质的金属度、粗糙度、反照率等参数直观看到三种材质对同一组参数的响应差异。右侧面板显示当前材质的 API 调用成本和 Draw Call 影响。直达链接http://localhost:18000/?ch13为什么这章很重要第 4 章你用过StandardMaterial第 12 章提到了 PBR 着色器的复杂性但从未解释过为什么需要多种材质。本章解答这个根本问题Phong 模型StandardMaterial 的基础有什么局限PBR 如何解决这些问题OpenPBR 又是为了什么而诞生理解三者的设计哲学后你才能在实际项目中做出正确的选型避免用错材质导致画面虚假或性能浪费。§13.2 原理从 Phong 到微表面模型13.2.1 StandardMaterial 的根基Phong 光照模型StandardMaterial是 Babylon.js 最老的材质类型基于Phong 光照模型1975 年由 Bui Tuong Phong 提出。Phong 模型把光照分成三部分环境光Ambient均匀照亮整个场景模拟间接光。漫反射Diffuse光线射到粗糙表面后向各个方向散射颜色取决于物体本身的颜色。镜面反射Specular光线在光滑表面上的高光反射形成亮斑。公式简化版最终颜色 环境光 × 环境系数 漫反射 × 漫反射系数 镜面反射 × 高光系数源码位置Materials/standardMaterial.pure.d.ts:45-65/** * Gets or sets the diffuse color of the material. */diffuseColor:NullableColor3;/** * Gets or sets the specular color of the material. */specularColor:NullableColor3;/** * Gets or sets the ambient color of the material. */ambientColor:NullableColor3;/** * Gets or sets the emissive color of the material. */emissiveColor:NullableColor3;Phong 模型的局限不能量守恒漫反射 镜面反射的总亮度可能超过入射光的亮度违反物理规律。视角依赖错误Phong 的高光计算假设观察者在特定角度当视角变化时高光形状会失真。没有粗糙度概念Phong 用单一的高光指数specularPower控制高光大小无法模拟真实世界中不同粗糙度的表面如磨砂金属 vs 抛光金属。不支持 IBLPhong 模型无法正确处理基于图像的光照Image-Based Lighting导致金属看起来像塑料。类比Phong 模型像一个只会画简笔画的画家——它能画出物体的大致形状和明暗但细节经不起推敲。当你把画面放大或改变光照角度时破绽就暴露了。13.2.2 PBRMaterial 的核心微表面模型与能量守恒PBRMaterialPhysically Based Rendering Material基于微表面理论Microfacet Theory。核心思想任何宏观上看似平滑的表面在微观尺度下都是由无数微小的镜面组成的。这些微表面的朝向分布决定了表面的粗糙度。PBR 的关键特性能量守恒反射的光能 ≤ 入射的光能。如果表面反射了很多光高金属度它吸收的光就少漫反射就弱。基于物理的参数金属度metallic0 非金属绝缘体如塑料、木材1 纯金属导体如金、银。粗糙度roughness0 完全光滑镜面反射1 完全粗糙漫反射。反照率albedo物体表面的基础颜色不含光照信息。菲涅尔效应Fresnel Effect grazing angle掠射角时反射率增加。这就是为什么你看水面时正下方能看到水底但远处只能看到天空的倒影。支持 IBLPBR 材质可以从环境贴图Environment Map中采样间接光照让金属活起来见第 14 章。源码位置Materials/PBR/pbrMaterial.pure.d.ts:40-56/** * Intensity of the direct lights e.g. the four lights available in your scene. * This impacts both the direct diffuse and specular highlights. */accessor directIntensity:number;/** * Intensity of the environment e.g. how much the environment will light the object * either through harmonics for rough material or through the reflection for shiny ones. */accessor environmentIntensity:number;/** * AKA Diffuse Texture in standard nomenclature. */accessor albedoTexture:NullableBaseTexture;/** * Specifies the metallic scalar of the metallic/roughness workflow. * Can also be used to scale the metalness values of the metallic texture. */accessor metallic:Nullablenumber;/** * Specifies the roughness scalar of the metallic/roughness workflow. * Can also be used to scale the roughness values of the metallic texture. */accessor roughness:Nullablenumber;关键理解PBR 的metallic和roughness不是独立的——它们共同决定了表面的反射行为。金属度高时漫反射几乎为零所有反射都是镜面的粗糙度高时镜面反射会扩散成模糊的光斑。13.2.3 OpenPBRMaterial标准化的妥协OpenPBRMaterial是 Babylon.js 9.x 引入的新材质类型基于OpenPBR 规范由 Adobe、Autodesk、Unity 等公司联合制定。它的目标是在不同引擎之间实现 PBR 材质的互操作性。为什么需要 OpenPBRBabylon.js 的 PBRMaterial 有太多专有扩展清漆层clearcoat、各向异性anisotropy、次表面散射subsurface等。这些功能很强大但导出到 glTF 时其他引擎可能不支持。glTF 2.0 的 PBR 标准有限只定义了baseColor、metallicRoughness、normal、occlusion、emissive等基础通道。高级通道需要 KHR 扩展如KHR_materials_clearcoat。OpenPBR 是一个折中方案它比 glTF 2.0 标准更丰富但比 Babylon.js 的完整 PBRMaterial 更克制确保最大兼容性。源码位置Materials/PBR/openpbrMaterial.pure.d.ts:383-410、:442-446/** * Color of the base diffuse lobe. * See OpenPBRs specs for base_color */accessor baseColor:Color3;/** * Roughness of the diffuse lobe. * See OpenPBRs specs for base_diffuse_roughness */accessor baseDiffuseRoughness:number;/** * Metalness of the base lobe. * See OpenPBRs specs for base_metalness */accessor baseMetalness:number;/** * Roughness of the specular lobe. * See OpenPBRs specs for specular_roughness */accessor specularRoughness:number;金属度和粗糙度叫什么名字OpenPBR 上叫baseMetalness和specularRoughness没有metallicFactor/roughnessFactor。后两个是 glTF 里pbrMetallicRoughness那组的字段名不是 Babylon 的材质属性实测metallicFactor in OpenPBRMaterial false写matOpenPBR.metallicFactor 0.8在 TypeScript 里直接编译不过不会「静默忽略」。还有一对容易混的baseDiffuseRoughness是漫反射叶的粗糙度OpenPBR 特有默认 0specularRoughness才是通常说的那个镜面高光粗糙度默认 0.3。OpenPBR 的限制v9.30.0 实测高级通道不是没有是换了名字而且摊平了清漆是coatWeight/coatColor/coatRoughness/coatIor/coatDarkening:596起各向异性是specularRoughnessAnisotropy:458次表面散射是subsurfaceWeight/subsurfaceColor/subsurfaceRadius另有transmissionWeight透光、fuzzWeight绒毛、thinFilmWeight薄膜。PBRMaterial 那一组子配置对象clearCoat/anisotropy/sheen/iridescence/subSurface在 OpenPBR 上一个都不存在实测全为false其中sheen、iridescence没有直接对应项最接近的分别是fuzzWeight和thinFilmWeight。标量都带非空默认值NullEngine 实测baseWeight1、baseMetalness0、specularWeight1、specularRoughness0.3、baseDiffuseRoughness0只设baseColor就看得见东西这跟 PBRMaterial 的metallic/roughness默认null正好相反见 §13.5。着色模型按 OpenPBR 规范实现跟 Babylon 自家 PBR 不同源同一组参数渲出的画面会有细微差别迁移完请重看画面而不是只改属性名。何时用 OpenPBR你的资产需要导出到 glTF 并在其他引擎Unity、Unreal、Three.js中使用。你不需要高级 PBR 通道只需要基础的金属/粗糙度工作流。你希望材质参数与行业标准保持一致方便与其他艺术家协作。何时不用 OpenPBR你需要清漆层汽车漆、各向异性拉丝金属、次表面散射皮肤、玉石等高级效果。你的项目只在 Babylon.js 内运行不需要跨引擎兼容。你需要极致的画面质量愿意牺牲一些兼容性。§13.3 三者迁移成本对比13.3.1 Standard → PBR难度中等步骤替换类名new StandardMaterial()→new PBRMaterial()。映射属性diffuseColor→albedoColorspecularColor→ 删除PBR 通过 metallic/roughness 自动计算ambientColor→ambientTexture如果需要环境光遮蔽emissiveColor→emissiveColor保持不变设置金属度和粗糙度非金属塑料、木材metallic 0roughness 0.5-1.0金属metallic 1roughness 0.0-0.3添加环境贴图否则金属看起来是黑色的// .hdr 走 HDRCubeTexture第三个参数 size立方图边长必填pbrMaterial.environmentTexturenewHDRCubeTexture(./assets/hdri/studio_small_03_1k.hdr,scene,1024)pbrMaterial.environmentIntensity1.0常见问题金属变黑因为没有环境贴图PBR 材质无法采样间接光照。解决方法添加 HDRI 环境贴图或使用scene.createDefaultEnvironment()。高光太强/太弱调整directIntensity和environmentIntensity。颜色变暗PBR 的能量守恒可能导致整体亮度下降。提高albedoColor的亮度或增加光源强度。13.3.2 PBR → OpenPBR难度低步骤替换类名new PBRMaterial()→new OpenPBRMaterial()。映射属性左边 PBRMaterial右边 OpenPBRMaterial两边名字都按 v9.30.0 的.d.ts抄过albedoColor→baseColoralbedoTexture→baseColorTexturemetallic→baseMetalnessroughness→specularRoughnessmicroSurface光泽度→specularRoughness两者是反的roughness 1 - glossiness别把同一个数直接填过去metallicTexture→baseMetalnessTexture粗糙度另走specularRoughnessTextureOpenPBR 上没有metallicRoughnessTexture这个属性emissiveColor→emissionColor单数变复数openpbrMaterial.pure.d.ts:793换掉不存在的属性clearCoat、anisotropy、sheen、iridescence、subSurface是 PBRMaterial 的 readonly 子配置对象pbrBaseMaterial.pure.d.ts:773-793在 OpenPBRMaterial 上这些名字一个都没有实测clearCoat in OpenPBRMaterial false写上去是 TS 编译错误不是「被忽略或抛出警告」。改法是用 OpenPBR 的顶层属性清漆coatWeight/coatRoughness/coatIor各向异性specularRoughnessAnisotropy次表面subsurfaceWeight/subsurfaceRadiussheen、iridescence没有对应项最接近的分别是fuzzWeight与thinFilmWeight。常见问题高级通道丢失清漆层、各向异性等效果消失。如果必须保留这些效果回退到 PBRMaterial。环境光照变化OpenPBR 的环境光照计算略有不同可能需要微调environmentIntensity。13.3.3 迁移决策树需要跨引擎兼容glTF ├─ 是 → 需要高级 PBR 通道清漆、各向异性等 │ ├─ 是 → 用 PBRMaterial KHR 扩展部分引擎可能不支持 │ └─ 否 → 用 OpenPBRMaterial ✅ └─ 否 → 需要极致画面质量 ├─ 是 → 用 PBRMaterial ✅ └─ 否 → 场景简单性能优先 ├─ 是 → 用 StandardMaterial ✅ └─ 否 → 用 PBRMaterial默认推荐§13.4 动手实验对比三种材质13.4.1 创建对比场景import{MeshBuilder,StandardMaterial,PBRMaterial,OpenPBRMaterial,Color3,Vector3,HemisphericLight,DirectionalLight}frombabylonjs/core// 创建三个球体constsphereStandardMeshBuilder.CreateSphere(mesh-sphere-std,{diameter:2},scene)sphereStandard.positionnewVector3(-3,1,0)constspherePBRMeshBuilder.CreateSphere(mesh-sphere-pbr,{diameter:2},scene)spherePBR.positionnewVector3(0,1,0)constsphereOpenPBRMeshBuilder.CreateSphere(mesh-sphere-open-pbr,{diameter:2},scene)sphereOpenPBR.positionnewVector3(3,1,0)// StandardMaterialconstmatStdnewStandardMaterial(mat-standard,scene)matStd.diffuseColornewColor3(0.8,0.2,0.2)// 红色matStd.specularColornewColor3(1,1,1)matStd.specularPower64sphereStandard.materialmatStd// PBRMaterialconstmatPBRnewPBRMaterial(mat-pbr,scene)matPBR.albedoColornewColor3(0.8,0.2,0.2)matPBR.metallic0.8matPBR.roughness0.2matPBR.environmentIntensity1.0spherePBR.materialmatPBR// OpenPBRMaterialconstmatOpenPBRnewOpenPBRMaterial(mat-openpbr,scene)matOpenPBR.baseColornewColor3(0.8,0.2,0.2)matOpenPBR.baseMetalness0.8matOpenPBR.specularRoughness0.2sphereOpenPBR.materialmatOpenPBR// 添加环境光consthemiLightnewHemisphericLight(light-hemi,newVector3(0,1,0),scene)hemiLight.intensity0.5// 添加方向光constdirLightnewDirectionalLight(light-dir,newVector3(-1,-2,-1),scene)dirLight.intensity1.013.4.2 观察差异运行后你应该看到StandardMaterial 球体高光是一个清晰的亮斑边缘锐利。改变视角时高光位置不变Phong 模型的缺陷。PBRMaterial 球体高光更柔和边缘有渐变。改变视角时高光形状和位置会自然变化符合物理规律。如果metallic 0球体会反射环境贴图的内容。OpenPBRMaterial 球体视觉效果与 PBRMaterial 非常接近但在极端角度或复杂光照下可能有细微差异。实验拖动滑块改变metallic从 0 到 1StandardMaterial无变化它没有 metallic 概念。PBRMaterial球体从塑料感逐渐变成金属感漫反射减弱镜面反射增强。OpenPBRMaterial类似 PBRMaterial但过渡可能略显生硬因为简化了计算。§13.5 预期输出与自查清单✅成功标志三个球体清晰可见颜色一致红色。PBR 和 OpenPBR 球体有明显的高光反射Standard 球体的高光较硬。拖动 metallic 滑块时PBR 和 OpenPBR 球体的外观实时变化。Inspector 显示三种材质的 Draw Calls 相同都是 1。控制台没有材质编译错误或警告。❌失败标志及修复PBR/OpenPBR 球体是黑色的缺少环境贴图。调用scene.createDefaultEnvironment()或手动设置environmentTexture。三种材质看起来一样metallic/roughness 没拉开差距。这里有个陷阱metallic/roughness的默认值不是 0.5 而是nullpbrMaterial.pure.d.ts:111、:116类型Nullablenumber两个都留空又没挂环境贴图时实测像素是纯黑tools/pxprobe.ts的合成场景syn:pbrmeanLuma 0不是「不高不低的中间灰」显式给metallic 0立刻就有漫反射syn:pbrm053.8。OpenPBRMaterial 恰好相反标量都带非空默认值只设baseColor就看得见。要对比三种材质把值调到极端0 或 1才分得清。Standard 球体没有高光specularColor设置为黑色或specularPower太低。确保specularColor new Color3(1, 1, 1)且specularPower 32。§13.6 常见坑与出处13-01PBR 材质在没有环境贴图时变黑现象设置metallic 1后球体变成纯黑色即使有方向光照射。原因PBR 的金属反射完全依赖环境贴图IBL。如果没有环境贴图金属无法反射任何东西看起来就是黑的。修复// 方法 1使用默认环境scene.createDefaultEnvironment({createSkybox:true,createGround:true,environmentIntensity:1.0})// 方法 2手动加载 HDRIconstenvTexturenewHDRCubeTexture(./assets/hdri/studio_small_03_1k.hdr,scene,1024)pbrMaterial.environmentTextureenvTexture pbrMaterial.environmentIntensity1.0方法 1 的两个createDefaultEnvironment()/createDefaultSkybox()是Helpers/sceneHelpers挂到Scene原型上的只按需 importbabylonjs/core的部分模块时它们可能不存在本书的分步示例因此手工搭天空盒。源码位置Materials/PBR/pbrBaseSimpleMaterial.d.ts:21-24。注释原文只有一句 “Environment Texture used in the material (this is use for both reflection and environment lighting).”并没有「如果没有设置金属将显示为黑色」这种话——那句结论是从着色模型推出来的不是引擎文档写的。现象本身是真的金属把漫反射项压到 0只剩镜面项而镜面项没有环境可采就什么都采不到于是整颗球是黑的。记住现象别记住那句注释。13-02specularPower 没有上限但它做不出金属现象设置specularPower 1000期望得到非常尖锐的高光实际看着却没什么变化于是断定「引擎把它钳到 128 了」。原因钳制这个说法在 v9.30.0 查无实据。specularPower就是一个普通的number属性默认 64赋值直接进 uniform中间没有任何范围检查所以「1000 被砍成 128」不成立。让它看起来「没效果」的是另外两件事一是高光斑的尺寸随指数增大而收敛几百以上已经缩到只剩几个像素再加只是那几像素更亮肉眼分不出 256 和 1000二是高光强度还受specularColor限制specularColor偏暗时再锐也看不见。更根本的是想要「金属感」本来就不该调specularPower那是 metallic/roughness 的活§13.2.2。修复specularPower想给多大给多大先确认specularColor够亮再分清你要的是「更锐的高光」还是「金属」——后者换 PBRMaterial 并降低roughness。源码位置Materials/standardMaterial.pure.d.ts:348声明就是accessor specularPower: number;没有 Range 注释、Materials/standardMaterial.pure.js:656默认 64、:1502ubo.updateColor4(vSpecularColor, this.specularColor, this.specularPower)原值透传指数装在颜色的 alpha 分量里13-03OpenPBRMaterial 的清漆不叫 clearCoat现象给 OpenPBRMaterial 写clearCoatTypeScript 报「属性不存在」换成 JS 或as any就不报错了但画面照旧于是得出结论「OpenPBR 不支持清漆赋值被静默忽略」。原因这个结论两头都错。OpenPBR有清漆而且比 PBRMaterial 的还完整coatWeight、coatColor、coatRoughness、coatIor、coatDarkeningopenpbrMaterial.pure.d.ts:596起。而clearCoat这个名字在两个材质上含义不同——在 PBRMaterial 上它是个 readonly 的子配置对象pbrBaseMaterial.pure.d.ts:773要写mat.clearCoat.intensity在 OpenPBRMaterial 上压根没有这个属性实测clearCoat in OpenPBRMaterial false。TS 报错是对的「静默无效」是你把类型系统绕开之后自己骗自己。修复按材质类选名字别跨类复制属性行。// PBRMaterial子配置对象默认关闭必须先 isEnabledconstmatPBRnewPBRMaterial(mat-pbr,scene)matPBR.clearCoat.isEnabledtrue// pbrClearCoatConfiguration.d.ts:47matPBR.clearCoat.intensity1.0// :51默认 1// OpenPBRMaterial顶层标量weight 默认 0为 0 就没有清漆constmatOpennewOpenPBRMaterial(mat-openpbr,scene)matOpen.coatWeight1.0matOpen.coatRoughness0.1matOpen.coatIor1.5顺带记住两个默认值的差异clearCoat.isEnabled默认falsepbrClearCoatConfiguration.js:148coatWeight默认0NullEngine 实测。所以「设了coatRoughness却忘了coatWeight」和「设了clearCoat.intensity却忘了isEnabled」都会得到同一个结果——完全没有清漆这才是「高级通道看不见」最常见的真凶跟支不支持无关。13-04迁移后颜色变暗现象从 StandardMaterial 迁移到 PBRMaterial 后整体画面变暗。原因PBR 的能量守恒限制了反射光的总量。StandardMaterial 可能作弊让漫反射和镜面反射的总和超过入射光看起来更亮但不真实。修复提高albedoColor的亮度例如从(0.8, 0.2, 0.2)改为(1.0, 0.3, 0.3)。增加directIntensity和environmentIntensity。添加更多光源或使用 HDR 环境贴图。§13.7 章节挑战挑战任务创建一个材质库展示台包含 6 个球体分别模拟以下真实世界材质抛光金metallic 1roughness 0.05albedoColor (1.0, 0.8, 0.2)磨砂铝metallic 1roughness 0.6albedoColor (0.8, 0.8, 0.8)红色塑料metallic 0roughness 0.3albedoColor (0.9, 0.1, 0.1)粗糙混凝土metallic 0roughness 0.9albedoColor (0.6, 0.6, 0.6)车漆带清漆层用 PBRMaterialmetallic 0.7roughness 0.1启用clearCoat.isEnabled true丝绸布料用 PBRMaterial启用sheen.isEnabled truesheen.color (0.9, 0.9, 1.0)验收标准6 个球体排列整齐每个球体下方有标签说明材质名称。抛光金球体有清晰的镜面反射能看到环境贴图的倒影。粗糙混凝土球体几乎没有高光表面呈现均匀的漫反射。车漆球体有两层高光底层的金属反射和表层的清漆反射。丝绸球体有柔和的边缘高光sheen 效果。提示清漆层和 sheen 是 PBRMaterial 独有的OpenPBRMaterial 不支持。确保场景有足够的環境光照否则金属和清漆效果不明显。§13.8 本章小结本章对比了 Babylon.js 的三种主流材质类型StandardMaterial基于 Phong 模型简单易用适合快速原型和低性能设备。局限是不能量守恒、不支持 IBL、高光不真实。PBRMaterial基于微表面理论能量守恒支持完整的 PBR 工作流金属度/粗糙度、清漆层、各向异性等。画面质量最高但需要环境贴图计算开销较大。OpenPBRMaterial基于 OpenPBR 规范在兼容性和功能之间取得平衡。适合需要跨引擎互操作的场景但不支持高级 PBR 通道。选型建议默认推荐PBRMaterial除非有特殊需求。需要跨引擎兼容 →OpenPBRMaterial。性能极度受限或快速原型 →StandardMaterial。下一章我们将深入 PBR 的各个通道讲解 albedo、metallic、roughness、ambient 等贴图的用途以及环境贴图如何让金属活起来。§13.9 术语卡中文English一句话解释Phong 模型Phong Lighting Model经典的光照模型分环境光、漫反射、镜面反射三部分微表面理论Microfacet Theory认为表面由无数微小镜面组成决定粗糙度和反射行为能量守恒Energy Conservation反射光能 ≤ 入射光能防止画面过曝金属度Metallic0 非金属1 纯金属决定漫反射和镜面反射的比例粗糙度Roughness0 完全光滑镜面1 完全粗糙漫反射反照率Albedo物体表面的基础颜色不含光照信息菲涅尔效应Fresnel Effect掠射角时反射率增加的现象IBLImage-Based Lighting基于图像的光照从环境贴图采样间接光OpenPBROpenPBR Specification跨引擎 PBR 材质标准平衡兼容性与功能清漆层Clear CoatPBR 的高级通道模拟表面透明涂层如车漆各向异性Anisotropy表面在不同方向有不同粗糙度如拉丝金属SheenSheen模拟布料等材质的柔和边缘高光本章字数统计非空白字符约 14,500 字折合 22.3 页按 650 字符/页计算。