在 3D 渲染中,"看不见的面该不该画"是个看似简单却暗藏玄机的问题。Babylon.js 的 PBRMetallicRoughnessMaterial 提供了三个相关属性,却让不少开发者陷入"我改的是哪个?为什么没效果?"的困惑。今天,我们来彻底拆解这三者的爱恨情仇。
一、三个属性的真面目
1. backFaceCulling:剔除功能的总开关
作用 :控制是否启用 面剔除,位于 Material 基类,影响所有材质类型。
TypeScript
const pbr = new BABYLON.PBRMetallicRoughnessMaterial("pbr", scene);
pbr.backFaceCulling = true; // 开启剔除(默认)
pbr.backFaceCulling = false; // 关闭剔除,正反面都渲染
核心认知 :这是最高层级 的控制,值为 false 时,另外两个属性直接失效。
2. cullBackFaces:剔除方向的选择器
作用 :在剔除启用时 ,决定剔除背面还是正面。
TypeScript
pbr.backFaceCulling = true; // 必须先开启剔除
pbr.cullBackFaces = true; // 剔除背面(默认)
pbr.cullBackFaces = false; // 剔除正面,只显示背面(魔法阵内部特效常用)
重要前提 :仅在 backFaceCulling = true 时生效,否则无意义。
3. doubleSided:几何层面的"真·双面"
作用 :物理上 为网格创建真实的双面几何,属于 Mesh 属性。
TypeScript
const mesh = BABYLON.MeshBuilder.CreatePlane("plane", scene);
mesh.sideOrientation = BABYLON.Mesh.DOUBLESIDE; // 会实际复制顶点数据
性能影响 :会双倍顶点数,但光照计算更精确。
二、三者的层级关系与决策链
TypeScript
┌─────────────────────────────────────────────
│ 几何层:mesh.sideOrientation (DOUBLESIDE?)
│ └─ 是 → 生成双面几何体
│ └─ 否 → 单面几何体
└──────────────┬──────────────────────────────
│
▼
┌─────────────────────────────────────────────
│ 剔除开关:backFaceCulling (true/false)
│ └─ false → 直接渲染所有面(结束)
│ └─ true → 进入剔除方向判断
└──────────────┬──────────────────────────────
│
▼
┌─────────────────────────────────────────────
│ 剔除方向:cullBackFaces (true/false)
│ └─ true → 剔除背面,显示正面
│ └─ false → 剔除正面,显示背面
└─────────────────────────────────────────────
一句话总结:几何决定"有没有",开关决定"剔不剔",方向决定"剔哪面"。
三、实战场景对照表
| 需求场景 | doubleSided | backFaceCulling | cullBackFaces | 效果说明 | 性能 |
|---|---|---|---|---|---|
| 标准单面渲染 | FRONTSIDE |
true |
true |
常见模式,背面不可见 | ★★★★★ |
| 快速双面可见 | FRONTSIDE |
false |
任意 | 关闭剔除,性能较好 | ★★★★☆ |
| 物理精确双面 | DOUBLESIDE |
true |
true |
正确法线,适合布料/纸张 | ★★★☆☆ |
| 仅看背面 | FRONTSIDE |
true |
false |
魔法阵内部、太空门内饰 | ★★★★★ |
| 玻璃窗户 | DOUBLESIDE |
false |
任意 | 双面+半透明,关闭剔除 | ★★★☆☆ |
四、PBR 材质的特殊陷阱
陷阱 1:光照计算错误
仅用 backFaceCulling = false 时,背面法线方向不变,导致 PBR 光照异常:
TypeScript
// ❌ 错误:背面看起来"反了"
pbr.backFaceCulling = false;
// ✅ 正确:配合 sideOrientation 翻转法线
mesh.sideOrientation = BABYLON.Mesh.BACKSIDE; // 或 DOUBLESIDE
pbr.backFaceCulling = false;
陷阱 2:glTF 标准兼容性
glTF 2.0 的 doubleSided 标志位等同于 mesh.sideOrientation = DOUBLESIDE,而非材质剔除设置。导入时需注意映射关系。
五、性能优化决策树
需要双面渲染?
├─ 是 → 模型本身是薄壳结构?(如纸张、树叶)
│ ├─ 是 → 建模时设为 DOUBLESIDE,保留 backFaceCulling=true
│ └─ 否 → 运行时动态切换?(如开关门)
│ ├─ 是 → backFaceCulling=false(性能更优)
│ └─ 否 → DOUBLESIDE(光照正确)
└─ 否 → 保持默认即可
性能结论 :backFaceCulling = false 只是逻辑屏蔽 ,顶点数不变;DOUBLESIDE 是物理翻倍,性能开销更大。
六、最佳实践速查
✅ 新项目推荐代码
TypeScript
// 1. 加载环境纹理(推荐 .env 格式)
const envTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("path.env", scene);
// 2. 创建天空盒(自动使用 PBRMaterial)
const skybox = scene.createDefaultSkybox(envTexture, true, 1000);
// 3. 普通模型需要双面
const cloth = BABYLON.MeshBuilder.CreatePlane("cloth", scene);
cloth.sideOrientation = BABYLON.Mesh.DOUBLESIDE; // 物理双面
✅ Legacy 项目兼容代码
TypeScript
const pbr = new BABYLON.PBRMetallicRoughnessMaterial("pbr", scene);
pbr.backFaceCulling = false; // 快速双面
七、总结:记住这个口诀
"开关管全局,方向定生死,几何定真身"
-
backFaceCulling:第一优先级,决定"要不要剔除" -
cullBackFaces:第二优先级,决定"剔除谁" -
doubleSided:独立体系,决定"模型本身几面"