用自动LOD简化3D网格【Babylon.js】

LOD(Level of Details)是一个强大的工具,在 2.0 版本中被添加到 Babylon.js 中。 这个概念相当简单:当相机距网格一定距离时,减少显示的面部数量将提高性能,而用户不会注意到这种减少。

LOD要求开发人员在原始网格上添加一些网格。 创建这些网格的方法之一是简化。 许多 3D 工具提供各种简化算法。 有时称为抽取。

从版本 2.0 开始,Babylon.js 提供了浏览器内简化功能,该功能异步工作,同时尽量不干扰渲染过程。 这些LOD级别将在计算完成后自动添加到场景中。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

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);

请参阅上面的解释以了解每个变量的含义。


原文链接:用自动 LOD 简化网格 - BimAnt

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值