17

重要提示:这个问题不是关于几何剔除(平截头体剔除、背面剔除、遮挡剔除或他们的任何朋友)。这个问题是关于设置时的几何消除,早在我们开始剔除和渲染之前。

在一个单位立方体渲染的世界(la MineCraft)中,我试图找到算法来从我的几何面列表中删除,无论相机在哪里,从任何角度都无法看到这些面。

例如,想象 2 个正方形:

+----+      +----+
|    |      |    |
|    |      |    |
+----+      +----+

显然有 8 个可见边(每个正方形 4 个。)现在我将正方形一起移动,可见

+----+----+
|         |
|         |
+----+----+

而不是有 8 个面,现在我只有 6 个!中间相碰的那两个,无论相机放在哪里,无论面对什么角度,都看不到。(正方形的纹理不同,所以我们不能称它为 4 面。)

(同样的事情在 3D 中也适用于立方体,但 12 个面(每个立方体 6 个)在消除 2 个接触后变为 10 个。)

我的问题是:有哪些算法可以帮助我识别这些隐藏的面孔?(我很高兴自己做谷歌搜索,但我什至不知道这叫什么!)特别是,我正在寻找可以处理中间空心点的东西——如果你是的话,这些点可能是可见的但是,因为它们被几何图形包围,所以你看不到它们。

例如:

+----+----+----+----+
|                   |
|                   |
+    +----+         +
|    |    |         |
|    | A  |         |
+    +----+         +
|                   |
|                   |
+----+----+----+----+

在这种情况下,人们可能会认为有 18 个“可见”边,但因为我们知道相机位于几何图形之外,所以正方形“A”中的 4 个边不可见。

更复杂的是,我希望找到一种算法,可以在添加或删除块时快速更新(同样,la MineCraft。)

谢谢!

4

1 回答 1

22

你问题的第一部分真的很简单。对于每个立方体,如果它与另一个立方体直接相邻,则删除它与该立方体共享的面。

这不应该是性能问题(除了修改和上传更改的顶点数据的成本),因为您只会在放置或删除块时重新计算它。放置和移除方块的效果是相当局部的;它只会影响 6 个相邻的立方体及其本身,所以它应该不是问题。除了处理基于多维数据集的环境所需的明显数据结构外,您也不需要任何专门的数据结构。

建造地形的初始成本可能是一些东西,但这是您可以忍受的一次性成本。将其纳入您的加载时间。如果您在框架空间中进行大量放置和移除,这可能是个问题。

更困难的问题是拆除密封的内部。我的建议:不值得。通过尝试移除密封的内部,放置或移除块成为非本地操作。通过花时间优化批处理计数(尽可能使用纹理图集/数组)和顶点数据,您可能会获得更高的性能。

要移除密封的内部,您需要能够检测内部。因此,您需要维护相邻面的双向图。对于每个面,它将有四个相邻的面。由于位于两个相邻立方体之间而被剔除的面(此前称为“死面”)不应成为图形的一部分。

放置立方体时,您必须更新人脸图的邻接信息。死面需要去除。放置后活面的邻接需要合并由于放置的新块而添加的新面。如果您坐下来找出可能性,那么执行此操作的算法应该相当简单。例如,如果你有这个正方形:

  A
+++++
+   +
+   + B
+   +
+++++

面 A 和 B 是相邻的。如果您放置一个新块,则会更改邻接关系:

     +++++
     +   +
   C +   +
     +   +
  A  +++++
+++++  D
+   +
+   + B
+   +
+++++

A现在与C相邻,B与D相邻。

移除立方体后,您必须再次更新人脸图的邻接信息。本质上,您必须反转先前的操作。

这个邻接图的要点是:如果图中所有的非死面都连接起来,那么图的恰好一个循环将是可见的;所有其他图形周期都将不可见。图的循环是一组直接或间接相互连接的面。

最大的问题是:你如何找到可见的循环?下面的算法假设方块是由实体放置/移除的。这意味着放置的任何块的至少一个面是可见的。这也意味着任何通过移除块而变得活跃的死面都是可见的。

当您放置一个块时,您可以创建一个或多个新的面循环。为了检测到这一点,您首先找到您通过放置块创建的所有非死面(不直接与某物相邻的面)。其中至少有一个是可见的;找到那张脸。

然后,对于新块上的每个其他非死面,使用 A*(A 星)图搜索来找到该面。A* 是一种基于优先级队列的图搜索算法。在这种情况下,算法的“距离”是当前人脸所在的正方形与可见人脸所在的正方形之间的距离。

如果 A* 找不到人脸,那么您就知道您搜索过的每个人脸(您可能应该在测试它们时将它们存储在缓冲区中)是不可见循环的一部分,因此可以被剔除。您应该将这些面标记为不可见以供以后参考。

删除块要容易得多。当你移除一个块时,块的至少一个面必须是可见的(参见上面的假设)。因此,如果要移除的块有一些不可见的面,则循环中的每个面(包括那些不可见的面)都必须变为可见的。因此,在移除块之前,请检查它是否有任何不可见的面。如果有的话,使用深度优先搜索来查找该循环中的所有面,并将它们放入缓冲区中。移除块后,所有这些面现在都变得可见。

现在,如果你能够传送方块,事情就会变得更加复杂。当您放置一个块时,该块的所有面都可能不可见。所以你需要做的是在世界的某个地方找到一张可见的脸。然后像往常一样对那张脸进行 A* 搜索。如果可见的脸在附近的某个地方会很好,所以搜索不必走得太远。

移除后,您要做的就是找到与块相邻的所有不可见面循环。然后你需要像以前一样找到那一张可见的脸。然后你移除块并用 A* 搜索这些循环,看看它们是否能找到可见的脸。那些可以看到的周期。那些不可能的周期是不可见的。

此外,您需要有一个特殊情况来删除没有活动面的块(即:完全嵌入其他块中)。这将创建一个不可见的 6 面循环。

也许现在你明白为什么它可能不值得努力了吗?老实说,除非你手头有实际的分析数据表明你需要这个,否则我强烈建议你不要这样做。你可能会做比必要的工作更多的工作,并且可能会在不相关的事情上花费大量时间。

现在,我即兴写了这篇文章;我想到了我能用的最简单的算法。我还没有研究过这个算法的可能改进,比如先发制人地找到可以创建内部的块放置,或者找到如果移除会使内部可见的块。所以我坦率地承认这个算法是相当蛮力的。但是找到一个更好的需要一些努力,所以再一次,除非你有分析数据说你要这样做才能达到你想要的性能,否则我建议不要这样做。

于 2011-06-12T04:49:12.440 回答