2

我正在 OpenGL 4.3.0 中实现体素光线投射器。我有一个基本版本,我将浮点值的 256x256x256 体素数据集存储在相同尺寸的 3D 纹理中。

但是,我想使用八叉树制作一个 LOD 方案。我将数据存储在主机端的一维数组中。根的索引为 0,根的子节点的索引为 1-8,下一级的索引为 9-72,依此类推。八叉树总共有 9 个级别(最后一个级别具有完整的 256x256x256 分辨率)。因为八叉树总是满的,所以结构是隐式的,不需要存储指针,每个体素只有一个浮点值。我已经设置好了一维索引和遍历算法。

我的问题是我不知道如何将它存储在纹理中。GL_TEXTURE_MAX_SIZE 太小(16384),无法使用我已经找到索引的一维数组方法。我需要将它存储在 3D 纹理中,我不知道当我尝试将我的 1D 数组塞入其中时会发生什么,也不知道如何选择尺寸和 1D->3D 转换方案以免浪费空间或时间。

我的问题是,是否有人有一个很好的策略来将整个八叉树结构存储在一个 3D 纹理中,在这种情况下如何为它选择维度和索引。

4

2 回答 2

3

首先是关于直接移植一维阵列解决方案的一些话:

首先,正如Mortennobel在他的评论中所说,最大纹理大小很可能不是 3397,这只是枚举值(定义该值的opengl.hGL_MAX_TEXTURE_SIZE标头应该如何知道您的硬件和驱动程序限制?)。要从您的实现中获取实际值,请使用. 但即便如此,这对你来说也可能太小了(可能是 8192 或类似的东西)。int size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);

但是要将更大的一维数组放入着色器中,您可以使用缓冲区纹理(自 OpenGL 3 以来它是核心,因此存在于 DX10 类硬件上)。这些是从标准 OpenGL 缓冲区对象中获取数据的纹理。但是这些纹理始终是一维的,由整数 texCoords(可以说是数组索引)访问而不是过滤。所以它们实际上不是真正的纹理,而是一种将缓冲区对象作为着色器内的线性 1D 数组访问的方法,这非常适合您的需求(实际上比普通过滤和标准化的 1D 纹理更适合) .


编辑:您可能还考虑像以前一样使用直接的 3D 纹理,但是对于层次结构的较高部分使用自制的 mipmap 级别(是的,3D 纹理也可以有 mipmap)。所以 mipmap 级别 0 是精细的 256 网格,级别 1 包含较粗的 128 网格,...但是要有效地使用此数据结构,您可能需要在着色器中进行显式 LOD 纹理访问(使用textureLod,或者甚至更好地不过滤,texelFetch),这也需要 OpenGL 3。


编辑:如果您不支持 OpenGL 3,我仍然不建议使用 3D 纹理将您的 1D 数组放入,而是使用 2D 纹理,就像Rahul在他的回答中建议的那样(1D-2D 索引魔法不是真的那么难)。但是如果你有 OpenGL 3,那么我要么使用缓冲区纹理直接使用你的线性 1D 数组布局,要么使用带有 mipmaps 的 3D 纹理进行直接八叉树映射(或者可能会提出一个完全不同且更复杂的数据结构首先是体素网格)。


编辑:当然,完全细分的八叉树并没有真正利用八叉树的内存节省特性来发挥其优势。对于将八叉树打包到 3D 纹理中的更动态和内存效率更高的方法,您还可以从这篇关于八叉树纹理的经典 GPU Gems 文章中获得一些灵感。它们基本上将所有八叉树单元作为 2x2x2 网格任意存储到 3D 纹理中,使用内部节点的值作为指向该纹理中子节点的指针。当然,现在您可以对此进行各种改进(因为您似乎也希望内部节点存储数据),例如将整数与浮点数一起存储并使用漂亮的位编码等,但基本思想非常简单。

于 2013-03-19T09:20:16.517 回答
1

这是一个解决方案草图/大纲:

使用 2D 纹理来存储 256x256x256(它将是 4096x4096——我希望您使用的是支持 4k x 4k 纹理的 OpenGL 平台)。

现在以行优先顺序存储您的一维数据。在您的光线投射器中,只需进行行/列转换(从 1D 地址到 4k x 4k)并查找您需要的值。

我相信你会自己弄清楚其余的:)

于 2013-03-19T03:38:38.143 回答