用自动 LOD 简化网格
LOD(Level of Details)是一个强大的工具,在 2.0 版本中被添加到 Babylon.js 中。 这个概念相当简单:当相机距网格一定距离时,减少显示的面部数量将提高性能,而用户不会注意到这种减少。
LOD要求开发人员在原始网格上添加一些网格。 创建这些网格的方法之一是简化。 许多 3D 工具提供各种简化算法。 有时称为抽取。
从版本 2.0 开始,Babylon.js 提供了浏览器内简化功能,该功能异步工作,同时尽量不干扰渲染过程。 这些LOD级别将在计算完成后自动添加到场景中。
1、用法 - 简化网格
BABYLON.Mesh 类的任何对象都具有具有以下签名的“simplify”函数:
public simplify(settings: Array<ISimplificationSettings>,
parallelProcessing: boolean = true,
type: SimplificationType = SimplificationType.QUADRATIC,
successCallback?: () => void);
参数说明如下:
settings
:设置对象有两个参数:
- quality - 0.0 到 1.0 之间的数字,定义抽取的百分比(1 表示 100%)
- distance - 距要添加此 LOD 网格的对象的距离。
- optimizeMesh(自 2.1 起) - 网格应该优化吗? 可选,默认为 false。 稍后将详细介绍优化。
settings
数组的一个简单示例是:
[
{ quality: 0.9, distance: 25, optimizeMesh: true },
{ quality: 0.3, distance: 50, optimizeMesh: true },
];
对于TypeScript用户和“new”爱好者来说,存在一个 SimplificationSettings 类。 所以,也可以这样做:
const settings: Array<ISimplificationSettings> = []; //in JS: const settings = [];
settings.push(new BABYLON.SimplificationSettings(0.8, 60));
settings.push(new BABYLON.SimplificationSettings(0.4, 150));
parallelProcessing
:该代码异步运行。 并行处理标志设置每个级别的处理顺序。 如果设置为 true,则所有内容都将一起运行。 这将使用更多的 RAM(在一段时间内),但通常会运行得更快。 然而,由于帧之间的许多并行计算,FPS 可能会降低到不可接受的水平。 将此标志设置为 false 将处理一个又一个的设置。 这将仅使用一个简化对象,并且将使用更少的 RAM。 然而,这可能需要更长的时间。
type
:为了允许实现进一步类型的简化(对于感兴趣的人将在后面解释),应该说明简化的类型。 目前只有一种,BABYLON.SimplificationType.QUADRATIC。 如果类型未定义,这也是默认值。
successCallback
:由于这是一个异步函数(立即返回),因此需要回调才能在简化过程结束后运行代码。Auto-LOD 过程成功完成后将调用此函数。
使用示例:
BABYLON.SceneLoader.ImportMesh("", "./", "DanceMoves.babylon", scene, (newMeshes, particleSystems, skeletons) => {
newMeshes[1].simplify(
[
{ quality: 0.9, distance: 25 },
{ quality: 0.3, distance: 50 },
],
false,
BABYLON.SimplificationType.QUADRATIC,
function () {
alert("LOD finisehd, let's have a beer!");
},
);
});
简化完成后,你还可以利用网格类的 getLODLevelAtDistance 和 getLODLevels 函数来访问简化后的网格。 你可以使用它来克隆简化网格并独立于主网格使用它。
2、规则和注意事项
并非所有网格都可以简化。 更好地说 - 所有网格都可以简化,但有些网格不应该简化。
像 Box 这样的对象(如果以最佳方式构建,如 BABYLON.MeshBuilder.CreateBox 函数)没有可以删除的“额外面”。 移除一个面将导致它……不再是一个盒子。
需要遵循的一些“规则”(永远不要忘记,规则就是用来打破的!)
- 尝试简化具有超过 500 个面的网格。 低于这个数字可能就没有意义。
- 最适合简化的网格是复杂的对象。
- 像平面这样的网格在简化后可能会失去形状。
- 网格质量越低,距离应该越远。 这不应该是线性的 - 简化到 90% 的复杂网格可能看起来仍然几乎相同,但在 30% 时,很明显网格被简化了。 较低质量的简化应该定义较大的距离。
- 尝试简化代表单个对象的网格。 一个包含许多远处物体的网格会简化得相当差。 LOD 在此类网格上的效果也很差,因为网格的位置实际上并不是所有网格部件的位置。
- 在开始抽取之前设置网格的材质。 LOD 网格在初始化时使用它们。 如果不设置它们,则预计材料会在达到给定距离时消失。
小技巧:
- 二次简化可以使用许多因素来计算。 位置、法线、颜色、UV坐标等...因素越多,运行速度就越慢(计算量越多)。 我们决定仅保留位置 - 这意味着简化后,UV 坐标有时会有点偏差。 如果遵循上述规则 4,通常不会引起注意。
- 网格可能会改变其形状。 对于小平面来说非常引人注目。网格中可能会突然出现“洞”。 使用网格优化可以避免这种情况(从 2.1 开始,面进一步描述)
- 从 Babylon.js 2.1 开始支持子网格。 由于缺乏边界检测,具有子网格的网格不会被 100% 正确地抽取(参见下一点)。 尝试一下,看看它是否符合你的需求。
- 边界上的一些三角形将被“删除”。 原因通常是(缺乏)边界检测,这是原始论文的一部分。 由于正确计算所需的时间,该功能未包含在实现中。
- 使用图像初始化的对象(最好的例子是基于高度图的地面)只会在图像完全加载后才会减少。 为此,请利用它们的 onReady 回调:
const ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap("ground", "textures/heightmap.png", {width: 20, height: 20, subdivisions: 100, onReady: (readyMesh) => {
//Simplify mesh here
readyMesh.simplify([{quality: 0.1, distance: 10}]);
...
}}, scene);
3、网格优化(从 Babylon.js 2.1 开始)
由于 Babylon 文件格式的性质,许多顶点可能具有相同的位置、法线,但具有不同的颜色/uv 信息。 这给抽取过程带来了问题,抽取过程依赖于改变具有相同位置的所有三角形。
如果你尝试简化网格,但它突然缺少一些三角形,这就是原因 - 你的网格没有针对简化进行“优化”。
有两种类型的优化可用:
- 全局改变函数,它是 BABYLON.Mesh 的一部分:
mesh.optimizeIndices(function () {
//do whatever you want here
});
该选项改变(!)网格的索引顺序。 它速度更快,但可能会更改网格顶点的 UV 坐标。 如果是这种情况,请使用:
- 简化过程中的优化 - 简化设置现在包含一个新变量:optimizeMesh,它是一个默认为 false 的布尔值。 如果设置为 true,则在网格准备抽取期间将运行不改变网格优化。 简化将在临时顶点数组上运行,并将新顶点的位置与旧的 uv/颜色信息相关联。 这是更好的选项,但也是较慢的选项(对于像演示头骨这样非常大的网格会很明显)
检查哪一种方法最适合你。 两者都有其优点和缺点,为了获得更好的结果,请使用第二个选项(在设置中将 optimizationMesh 设置为 true)。
4、开发进一步的简化算法
如果要添加新的简化算法,需要执行以下几个步骤:
- 创建一个实现 BABYLON.ISimplifier 接口的类(当然还要实现该函数!:-))
- 将简化类型添加到 SimplificationType 枚举
- 在 mesh.simplify 函数中添加类 init (内部函数“getSimplifier”应包含您的类型)。
- 我们很高兴看到采用你的新简化方案的 PR!
5、直接访问简化类
可以直接访问二次误差抽取并使用其功能,通过创建 QuadraticErrorSimplification
类的对象来做到这一点:
const decimator = new QuadraticErrorSimplification(meshToDecimate);
之后你可以使用以下对象变量:
- decimationIterations - 抽取过程的最大迭代次数。 简化(通常)会在最大迭代次数之前停止,但实际上一切都取决于下一个变量。 默认值:100。
- aggressiveness - 决定三角形是否可以删除的阈值是使用此变量决定的。 将其设置为较低的值(例如 2)将导致抽取速度稍慢,但在选择三角形时会更加精确。 将其设置为较高值(例如 15)可能会导致抽取在 1 或 2 次迭代中结束,因为将选择删除大量三角形。 无论如何,一旦达到质量,该过程就会停止。 问题是——它是否选择了更好的三角形来消灭! 默认值:7。
- syncIterations - 异步迭代内同步迭代的数量。 很难解释,但是较低的数字对性能的影响较小,但会使该过程花费更多的时间。 如果您需要解释,请查看babylon.tools.js 中的AsyncLoop 类或在论坛上联系@raananw。 设置变量后,可以运行简化函数来启动整个过程:
simplify(settings, successCallback);
请参阅上面的解释以了解每个变量的含义。
原文链接:Simplifying Meshes With Auto-LOD
BimAnt翻译整理,转载请标明出处