生成用于地形生成的Octree


9

我以前已经实现了行进立方体/四面体以渲染IsoSurface。它可以正常工作(YouTube),但是性能却很糟糕,因为我从来没有绕过根据视距实现可变的“细节级别”(甚至删除了旧的,遥远的块)。

我决定再次尝试一下,这次要正确地做。我首先创建了一个OctreeNode,它在Build()被调用时的工作方式如下。

  • 如果块太小而无法构建,请立即返回。
  • 算出表面是否穿过该块的体积。
  • 如果是这样,则决定我们是否要提高LOD(因为相机已关闭)
  • 如果是这样,则生成8个子代并对其调用相同的过程
  • 如果没有,请使用当前节点的尺寸构建网格

一些伪代码:

OctNode Build() {
    if(this.ChunkSize < minChunkSize) {
        return null;
    }
    densityRange = densitySource¹.GetDensityRange(this.bounds);
    if(densityRange.min < surface < densityRange.max) {
        if(loDProvider.DesiredLod(bounds > currentLoD) {
            for(i 1 to 8) {
                if(children[i] == null) {
                    children[i] = new OctNode(...)
                }
                children[i] = children[i].Build();
            }
        } else {
            BuildMesh();
        }
        return this;
    }
}

¹不仅可以返回某个点的密度,还可以确定给定体积的可能密度范围。

²LoD提供程序带有一个边界框,并根据摄像机的位置/视锥,用户设置等返回最大所需的LoD。

所以...这一切都很好。使用简单的球体作为“密度”源,并显示所有节点:

全八进制

只是叶子:

只显示叶子的八进制

但是,有两个问题:

  • 我必须定义初始边界体积(并且它越大,我需要做的处理越多)
  • 在树的根部,我不知道叶子的深度,所以我的LoD编号从最低质量(根)开始,并随着块的变小而增加。因为LoD现在相对于初始体积,所以当我想以特定的大小/质量进行操作时,它的用处不大。

我想到了几种选择,但两种选择似乎都有缺陷:

  • 维护八位字节的集合,并根据距离添加/删除。无法看到如何很好地进行网格划分¹,此外,我还需要一个已知的空节点列表,特别是如果我想要任意3D曲面(以避免重复重新计算空体积)时
  • 将父节点添加到当前根,然后为原始节点添加七个同级。这可以正常工作,并且可以按需进行,但是随着玩家在景观中移动,明智地收缩下来似乎很复杂。这也将使LoD编号的意义降低。

¹[为澄清下面的问题,目前,如果树中2个物理上相邻的节点位于不同的LOD,则我有一些代码可以强制转换顶点,以便在生成网格时不会出现接缝。我可以通过了解多个周围节点的密度来做到这一点。在并排有2个独立八叉树的情况下,我没有简单的方法来检索此信息,从而导致接缝。

解决此问题的最佳方法是什么?

Answers:


1

我不确定我要回答的是确切的问题,因此我将分段回答,如果对特定问题的细节有误解,请随时在评论中回复。

我必须定义初始边界体积(并且它越大,我需要做的处理越多)

似乎并非如此。由于八叉树的粒度呈指数级增长,因此在顶部添加几个级别应该对性能造成的影响是很小的净增长。

在树的根部,我不知道叶子的深度,所以我的LoD编号从最低质量(根)开始,并随着块的变小而增加。因为LoD现在相对于初始体积,所以当我想以特定的大小/质量进行操作时,它的用处不大。

如果将根八叉树固定为某个“足够大的值”,那么“相对于初始体积的LoD”就不成问题。而且如上所述,我认为高层没有额外的水平会影响整体性能。

维护八进制的集合,并根据距离添加/删除。无法看到如何很好地进行网格划分,此外,我还需要一个已知的空节点列表,尤其是在需要任意3D曲面的情况下(避免重复计算空体积)

据我了解,此提议的解决方案是在远离先前的详细区域时降低LoD。我认为它应该看起来与“ LoD增加”代码路径非常相似:

if (loDProvider.DesiredLod(bounds) <(is a lot less than)< currentLoD) { 
    for(i = 1 to 8) { 
        children[i].Destroy();
    }
    BuildMesh();
}

然后,您不必花费太多时间检查远程节点,因为您可以依靠这样的事实,即在很远的地方,“活动”节点不会太多,因为所有高分辨率节点都将被删除。


假设小节点是岩石级的,那意味着当我穿越大洲时,可能意味着数十个甚至数百个水平

我认为八叉树的对数尺度使其仍然可行。如果您的顶层是1,000,0000,000米宽(这将是比真实的地球更广泛的25倍,并与625X的面积)和你的最低水平位是跨10CM,这是在八叉树,这是32级大概易于管理。如果您想要一个比地球宽100倍的行星(并且表面积增加10000倍),那么八叉树只需要增加3-4级。在这一点上,玩家需要花费数百年的时间才能遍历整个世界,如果您使用幼稚的浮点数学,世界将累积精度误差。

维护八位字节的集合,并根据距离添加/删除。无法看到如何很好地进行网格划分¹,此外,我还需要一个已知的空节点列表,特别是如果我想要任意3D曲面(以避免重复重新计算空体积)时

这从根本上讲不等于拥有十亿公里宽的八叉树,而是保留指向每个(例如1公里)街区的指针的列表?然后,“跨网”将仅依赖于2 km大小的节点。如果您担心“可能还有数十个额外的八叉树等级”,则对每个中级“大块”进行本地引用还可以使您不必遍历顶级节点。


谢谢回答。我不能随便选择一个巨大的初始体积,因为我的地形是无限的(这就是我的目标)。观看视频以了解点子。这样,我的节点树将变得越来越高。假设小节点是岩石级的,那么当我穿越一个大陆时,可能意味着数十个甚至数百个级别。回复:啮合,让我在问题[完成]中添加更多详细信息
基本的

就玩家而言,“十亿英里”非常接近“无限”。固定初始体积的更多参数已添加到答案中。
吉米

我还是不服气。为什么要进行30层以上的无用的已知处理?它并不优雅,更不用说高效了。我同意您的意思,说的是步行的时间,但我们只是在谈论地形的产生。没什么可以说我必须以原点为中心的,也不是不可能以高速飞行(尽管我不会以这种速度啮合!)。FWIW我在内部使用double来避免精度问题。
基本
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.