应该使用哪种数据结构来表示体素地形?


18

根据维基百科有关体素的页面,“根据体素相对于其他体素的位置(即,其在构成单个体积图像的数据结构中的位置)来推断体素的位置。”

一个人应该如何实现这样的数据结构?我当时正在考虑使用八叉树,但我想知道是否还有其他我从未听说过的东西。


1
这是一个很难回答的问题,因为它非常取决于您的体素数据将需要什么数据。像体素有多饱满,它看起来像什么等东西都将完全取决于您的工作。其次,数据结构必须使其能够进行高速访问,以进行数据的实时处理和随后的更新。如何保持每个体素的内存结构较低以及如何快速进行数据快速访问/操纵是当前面临的主要技术挑战。使用体素引擎时,其意图非常明确。不是答案,而是评论。
詹姆斯

Answers:


14

第一。让我们写下我们对每个体素的了解:

voxel = (x, y, z, color) // or some other information

一般储存

一般的方法就是这样:

set of voxels = set of (x,y,z, color)

请注意,三元组(x,y,z)唯一地标识每个体素,因为体素是空间中的点,并且不可能有两个点占据一个位置(我相信我们正在谈论静态体素数据)。

对于简单数据应该没问题。但这绝不是一种快速的数据结构。

渲染是通过扫描线算法完成的AFAIK。汤姆在体素上的硬件文章中有scanline算法的图像

快速查询

如果需要快速查找,则用于查找的最快数据结构是哈希(aka数组,映射...)。因此,您必须利用它进行哈希处理。因此,我们天真地希望获得最快的方法来获取任意元素:

array [x][y][z] of (color)
  • 它具有O(1)用于通过x,y,z坐标查找体素。

  • 问题是,它的空间要求是O(D ^ 3),其中D是每个x,y和z数的范围(忘记实数,因为如果它们是Char,具有256个值的范围,则将有256 ^ 3 = 2 ^ 24 == 16777216个元素)。

但这取决于您要对体素做什么。如果渲染是您想要的,则可能是您想要的此数组。但是存储问题仍然存在...

如果存储是问题

一种方法是在阵列中使用RLE压缩。想象一下一个体素切片(一组体素,其中体素具有一个坐标常数值.....例如,z = 13的平面)。这样的体素切片看起来像MSPaint中的一些简单绘图。我会说,体素模型通常占据所有可能位置的一部分(所有可能体素的D ^ 3空间)。我相信,“从三元组的坐标中获取一对并压缩剩余的轴”将达到目的(例如,取[x] [y]并为每个元素在给定的x,y下压缩z轴上的所有体素。 。应该有0到几个元素,RLE在这里可以做得很好):

array [x][y] of RLE compressed z "lines" of voxel; each uncompressed voxel has color 

解决存储问题的另一种方法是使用树数据结构代替数组:

tree data structure  = recursively classified voxels
for octrees: recursively classified by which octant does voxel at (x,y,z) belong to
  • 尼克提到的八度。它应该压缩体素。Octree的查找速度甚至还不错,我想它是O(log N),其中N是体素数。
  • Octree应该能够存储体面的任意体素数据。

如果体素是一些简单的高度图,则可以将其存储起来。或者您可以将参数存储到生成高度图的函数,也可以按程序生成高度图...

当然,您可以组合所有可能的方法。但是不要过度使用它,除非您测试您的代码是否正常运行并确定它确实更快(因此值得进行优化)。

TL; DR

除了Octrees以外,还有使用体素,Google“ voxlap”,“ ken silverman”的RLE压缩...

资源资源

这里有资源清单和有关如何制作快速体素渲染器的讨论,包括论文和源代码


1
“如果存储的问题”:你也可以使用VTC(oss.sgi.com/projects/ogl-sample/registry/NV/...)或DXT压缩
KindDragon

@KindDragon感谢您提供的信息。:)这是一个很好的主意。
user712092 2011年

资源链接已断开。
Ezequiel 2014年

4

他们可能在谈论两个不同的数据结构方面。

数组结构

当您引用任意数量维度的数组的元素时,请考虑该数组本身,一旦传递索引(例如myArray[4][6][15]),便知道该位置的内容。如果该位置的是体素,则该体素不需要另外记录自己的x,y和z坐标-持有体素的数组会根据其数组索引位置隐式指定其世界位置。

之所以如此好,是因为用于这种数组访问的指针算法本质上是快速的,通常来说,它为跨语言找到的大多数快速(通常称为“本机”)数组提供了基础。这些数组的缺点是它们必须具有大小相等的字节元素,以使所述指针算法适用。

八进制

(我要注意第二点,因为这几乎不是维基百科所指的,并且体素实现不需要使用八叉树,尽管几乎所有现代人都使用八叉树。)

八叉树的根节点是单个不可分割的多维数据集。让我们建立一个例子。说八角的根,即立方体的正中心{0, 0, 0}位于3D空间中。一旦开始在该空间中放置对象(阅读:多个对象),就该进一步细分八叉树了。在这里,您可以使用3个平面将其切成8(八进制),这些平面是xy,xz和yz平面。现在,原始多维数据集正好包含8个较小的多维数据集。这些子节点中的每个子节点都与中央父多维数据集偏移。也就是说,例如,位于正xyz八分圆中的多维数据集与父/包含多维数据集的中心的偏移量为{root.width / 4, root.height / 4, root.depth / 4}。与其为每个子节点指定绝对位置,不如将父节点视为其子空间的原点更具逻辑意义。这与场景图的工作方式相同。

在2D绘图中看到一个正方形非常简单,您可以在其中绘制一个正方形并将其细分为4个相等的区域。如果像八叉树的根节点一样,将父正方形的中心视为{0, 0},则子正方形的4个中心将为

{root.width / 4, root.height / 4}{-root.width / 4, root.height / 4}{root.width / 4, -root.height / 4}{-root.width / 4, -root.height / 4}

...相对于其父级-与3D中的原理相同。


谢谢您的回答。在我的情况下,地形的很大一部分将由相同类型的体素制成,这就是为什么我要考虑八叉树的原因(不必细分一大块)。但是,我将给3D阵列一个镜头,因为它看起来更易于实现。我敢肯定,我可以设法抽象出我的Terrain类的实现细节,以便在需要时切换实现变得不那么困难。
pwny 2011年

别客气。我绝对建议您调查一下八叉树,实际上它们并不难掌握。但是,是的,您的方法目前有意义,当然值得使用3D阵列进行初始原型制作。
工程师

作为进一步的阅读,可以在英特尔的《扩展游戏的STL》一文中找到有关octree的实现的良好讨论,其中包括几个有用的参考。
Martin Foot

1

您可以使用RLE。但是您可以使用SVO(Sparse Voxel Octree),id Tech 6使用SVO。SVO是一种3D计算机图形渲染技术,使用射线投射或有时将射线跟踪方法转换为八叉树数据表示形式。

该技术有所不同,但是在给定屏幕分辨率和尺寸的情况下,通常依赖于生成和处理可见或可能可见的点(稀疏体素)的外壳。

使用光线投射,因为它更快。


0

通常,您可以避免地形的3D数据结构。您可以改用高度图。在运行时可以非常便宜和高效地对其进行体素化。按照我的经验,通常需要跟踪每列中需要渲染的最小高度,有时还需要跟踪起点到终点的角度,以便也可以剔除背面的列。

这是我回想很久的一个:http : //sites.google.com/site/williamedwardscoder/spinning-voxels-in-flash

如果您的地形有少量的悬突,山洞或其他无法由高度图表示的要素,那么您可以在高度图中有孔并具有其他表示形式,例如真正的3D体素对象,这些对象仅填充那些在运行时所花费的局部位置是有保证的。

当您拥有大量的真实体素世界时,稀疏体素表示是值得的。 约翰·卡马克(John Carmack)过去几年一直在谈论他们。


我也考虑过高度图,但是有几件事使我远离了高度图。问题是,就我而言,地形无论如何都不算​​是真正的大或非常复杂(想想迷宫式地形,非常笛卡尔)。我也希望地形的一部分是可破坏的,或者允许用户通过构建来影响地形。这可能会导致播放器在地形中创建“隧道”,用高度图表示似乎更加复杂。
pwny 2011年
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.