BabylonJS大规模场景优化

在本文中,我们将重点介绍用于优化 Babylon.js 海港场景的优化和架构技术。 我们的场景总共有超过 600 个网格和 1,000,000 个顶点。 在我们的 2018 Macbook Pro 上,它在 Google Chrome 中始终以 45+ FPS 的速度运行。 我们发现 Firefox 约为 40 FPS,而 Safari 则低得多,但仍可用,为 25 FPS,主要是因为它不支持 WebGL 2.0。

这里讨论的优化技术确实涵盖了 Babylon.js,但也侧重于改进我们使用的对性能有巨大影响的底层模型、材料和光照的方法。 我们所有的模型都是使用 Blender 构建的,因此,此处包含的示例通常参考 Blender 解决方案,但也可以使用任何其他 3d 计算机图形软件。

注意:可用的优化技术比这里描述的要多得多,有些可能不适用于你自己的环境(例如,我们的一些技术要求我们的模型在场景中是静态的)。 有关推荐技术的更多列表,请查看 Babylon 官方关于这个问题的文章

1、单个与多个模型导入

在为场景设计资产时需要权衡取舍。 以我们的 Harbor 为例。

我们可以在单个 .blend 文件中对整个场景进行建模,然后通过单个 .gltf 文件将其传输到 Babylon。 或者,我们可以在独立的 .gltf 文件中设计每个建筑物、船只、树木和岩石,然后在 Babylon.js 脚本中导入和定位资产。

更少的模型和网格意味着 Babylon 渲染循环要循环的更少。 在 3d 软件中安排场景也更容易,并且可以由不熟练的 JavaScript 程序员的团队成员完成。 但是,如果你有一个特别大的模型,加载时间可能会很长(对于上下文,我们最终的 .glb 文件是 8MB)。

或者,我们可以在自己的 .gltf 文件中构建我们的个人资产(建筑物、树木等),然后导入这些文件并使用 JavaScript 来定位它们。 这在设计布局和空间方面不如在自己的 3d 软件中实用,但在加载时间方面有所收获,因为模型可以并行加载。 它还可以轻松利用 Babylon 自己的实例化功能,从而进一步优化树的放置。

在 MAS 数字体验中,我们选择了一种混合方式。 大多数情况下,我们依赖于在 Babylon.js 之外构建的资产,而是在 Blender 中。 我们发现让非开发人员自由编辑场景太有用了,不能牺牲。 我们对加载时间感到满意,因为体验总是预加载了介绍性动画,因此在后台发生的场景加载不太明显。 此外,我们知道我们想要在 Blender 中对我们的场景进行高质量渲染。 在那里构建完整场景意味着静态渲染完美地反映了基于浏览器的世界。

2、使用 Babylon.js 监控性能

为了进行优化,我们需要在整个开发过程中注意我们的表现。 虽然 Babylon.js 有自己出色的调试检查器,但我们发现在开发中有用的最简单的资源是创建我们自己的 FPS 计数器,它始终位于角落。 Babylon 引擎可以公开此值。

当我们向场景添加新资源时,当我们在做一些对性能有害的事情时就会变得非常清楚。 此外,在有意识地优化我们的场景时,这提供了一个非常易于阅读的改进指标。

不过,请务必在执行此操作时与你的场景进行交互。 仅仅因为你的场景在相机静止时以 60 FPS 运行,并不意味着它会保持这种状态。 四处走动,点击东西,做你的用户会做的事情!

如果你正在努力寻找性能影响发生的地方,我们在 Inspector 中发现了这些非常有用的 Babylon 功能,它们对你有所帮助:

2.1 线框和点渲染

在 Babylon 的 Inspector 中,我们可以选择更改渲染模式。 你可以通过在场景代码中包含以下行来查看检查器:

scene.debugLayer.show()

从现在显示的菜单中,单击左侧“场景资源管理器”中的“场景”对象。 在右侧的“渲染模式”下,你可以在“点”、“线框”和“实体”之间切换。 我们发现的这些替代视图提供了非常清晰的多边形密度图像。

在这种情况下,我们可以看到该图像中心的建筑物代表 IBM 的运营决策管理器,考虑到它包含的边数和多边形数量,优化仍然很差。

2.2 显示/隐藏网格和已启用切换

Inspector 的另一个最有用的功能是能够打开/关闭场景中的每个网格。 这提供了一种非常简单的方法来监控特定资源对场景性能的影响,这可能与多边形计数无关。

关于如何执行此操作有两种选择:

  • “场景资源管理器”中任何网格右侧的“眼睛”图标。
  • Babylon TransformNode 的“IsEnabled”开关。 单击场景资源管理器中的变换节点后,此选项将显示在右侧检查器的“常规”选项卡中。

在 Blender 中,我们发现将对象组父子化到单个 Empty 创建的变换节点。 这些在这里特别有用,因为当导入到 Babylon 时,可以打开/关闭每个节点的“isEnabled”选项,因此一次点击所有子节点,使得寻找潜在的性能改进变得更加容易。 Blender 集合在导出到 gltf 时丢失。

启用全场景后,我们达到了 48 FPS。

在我们的产品相关建筑物的 Inspector 中关闭“isEnabled”选项时,对 FPS 的影响很小——现在为 49 FPS。

但是,当关闭我们的“填充”建筑物时,我们注意到一个显着的改进,帧速率跃升至 59/60 FPS。 显然,这些网格是我们需要集中注意力并进一步优化的地方。

3、标准 3D 优化技术

我们应该在此处遵循适用于任何实时渲染场景的标准良好实践,无论是 Babylon.js 还是基于浏览器的场景。

3.1 多边形计数

要考虑的最简单的指标是多边形计数。 渲染 4 个顶点比渲染 4,000,000 个要快得多。 在可能的情况下,对你的模型要聪明,只在重要的地方添加细节。 如果你的相机缩小到永远看不到它,那么在角色模型上雕刻高清面部是没有意义的。

Ian Hubert 的 1 分钟“懒惰”Blender 教程是一个很好的例子,说明即使使用超低多边形网格也可以获得多少“细节”。 为 4k 渲染和高预算电影构建高清模型非常好,但如果你希望构建基于浏览器的体验,那么请灵活地确定你的模型在何处以及如何提供细节。

岩石模型重新拓扑的“前后”视图

如果你有一个高多边形模型并希望对其进行优化,那么重新拓扑是你的朋友。 这可能是一个乏味的过程,但性能改进是显着的。

如果你的建筑物有窗户或门等细节,那么你还可以烘焙法线贴图以提供细节外观,而不会减少顶点数。 法线贴图在不牺牲多边形计数的情况下添加高细节特别有效。

左:应用了法线贴图的模型; 中间:没有法线贴图的模型; 右:法线贴图

值得注意的是 .gltf 网格具有三角面,因此当你将模型移至 Babylon 时看到大量“面数”跳跃时不要感到惊讶。 在可能的情况下,在导入之前对面进行三角剖分,以确保你可以控制多边形和面数。

3.2 实例化

如上所述,渲染 4 个顶点比渲染 4,000,000 个要快。 我们可以“欺骗”我们的渲染器认为我们正在使用更少的顶点的一种方法是通过实例化。 这减少了 Babylon 在每次渲染中必须经历的绘制调用总数。

在我们完成的海港场景中,我们有 631 个活动网格和 73 个绘制调用。 在没有实例化的情况下,我们预计会有 631 次绘制调用,并且在我们的案例中渲染速度会慢 5-10 倍。

实例化是使用硬件加速渲染绘制大量相同网格(让我们想象一片森林或一支军队)的绝佳方式。 这可以在你选择的 3d 软件中完成(我们使用 Blender,可以使用链接副本实现),或者直接在 Babylon.js 中完成,如果你的场景适合从内部定位对象的用例 JavaScript。

在一个场景中多次实例化和重复使用同一个模型的一个负面后果是,很明显你一遍又一遍地冲洗和重复同一个网格。

在这里,我们分享了一些来自 Blender 的示例渲染,展示了如何利用实例化,同时保持模型的规模感和可变性。

左:16 个顶点(实例化); 16k 个顶点(无)——中间:48 个顶点(有); 48k 个顶点(无)——右:228 个顶点(有); 228k(不含)

在我们的第一张图片中,我们有一个实例化了 1,000 次的建筑物。 在第二次渲染中,我们使用建筑物的 3 种材质变体,平均分配给 1,000 个实例。 最后,在第三张图片中,我们修改了建筑物,使其不对称,并引入了围绕垂直轴的随机旋转。 这些微小的变化带来了可变性,同时保持了优化的几何形状。

4、WebGL 的额外注意事项—材质合并

使用 Babylon 时,通常使用 .gltf 格式导入文件。 不过你可能会注意到,当模型加载到 Babylon 时,它会将i的模型拆分为多个单独的对象。

这是在逐个材质的基础上发生的,这意味着在渲染循环中,Babylon 每次都会循环遍历每个新创建的对象,而不仅仅是单个原始网格——代价高昂!

理想的解决方法是尽可能少地使用材料,虽然你可能希望每个对象都具有漂亮的 4K 纹理和 PBR 材料,但如果可以并且符合想要的审美,你可以使用调色板。

左图:从 IBM Carbon Palette 创建的多种调色板。 右图:我们用于港口场景的调色板,其中包括渐变段。

使用调色板,你可以为所有对象定义单一材质。 将该材料的基础/反照率颜色设置为调色板纹理,并确保模型的 UV 面与你想要的颜色对齐。

为此,在你的 3d 软件中,UV 展开对象,将对象的 UV 缩小到零,然后将 UV 移动到与你希望该面的颜色相匹配的彩色方块中。 如果你使用的是 Blender,Imphenzia 会在他的 10 分钟 Blender 挑战中经常这样做,他在这里详细介绍了这项技术。

每个调色板只需 3x3 像素(9 种不同颜色)或 8x8 像素(64 种颜色)。 你也可以选择更大的调色板,这样你就可以选择将渐变添加到调色板中。 如上所示,我们在五月花号体验中将这种技术用于港口的调色板。

5、Babylon.js 技巧与技巧

如前所述,Babylon 的文档包含丰富的解决内存使用和性能问题的方法。 我们发现了两个特别有用的资源:

具体来说,我们在场景和适当的网格导入中使用了以下方法:

5.1 场景:禁用对象交互

如果在你的场景中,不需要直接与 3d 网格交互,那么我们发现禁用鼠标事件非常有效。

scene.pointerMovePredicate = () => false;
scene.pointerDownPredicate = () => false;
scene.pointerUpPredicate = () => false;

我们的 Harbor 和 Challenge 3 场景中使用了这种方法。 我们不能在挑战 1 中使用它,因为我们需要检测用户点击环境对象(岩石、浮标和船只)。

5.2 场景:移除缓存的顶点数据

所有顶点缓冲区都在 CPU 内存中保留其数据的副本,以支持碰撞、拾取、几何编辑或物理。 如果你不需要使用这些功能,你可以调用这个函数来释放相关内存:

scene.clearCachedVertexData();

5.3 网格导入:静态资产

如果有不会改变位置、旋转或大小的网格,那么通过调用“冻结”网格会变得非常有效:

mesh.freezeWorldMatrix();

即使它们确实发生变化,但会间歇性地发生变化,你也可以通过调用以下命令重新打开世界矩阵计算:

mesh.unfreezeWorldMatrix();

5.4 网格导入:无交互

如果不需要用户单击或拾取你的网格,则可以在导入网格时添加以下行以进一步提高性能:

mesh.isPickable = false;
mesh.doNotSyncBoundingInfo = true;

5.5 VueJS 和Babylon.js的组合问题

在我们的项目完成后,我们实际上发现了使用 BabylonJS 和 VueJS 的陷阱。 虽然我们对这里的 FPS/性能非常满意,但事实证明我们通过将 BabylonJS 引擎和场景绑定为 VueJS 中的反应变量导致了巨大的 FPS 不足。

我不会在这里详细介绍,但这篇论坛帖子中对此进行了非常详细的介绍。


原文链接:Optimizing a Large-Scale Babylon.js Scene

BimAnt翻译整理,转载请标明出处