我正在 OpenGL 4.3.0 中实现体素光线转换器。我有一个基本版本,我在相同尺寸的 3D 纹理中存储浮点值的 256x256x256 体素数据集。
但是,我想使用八叉树制定 LOD 方案。我将数据存储在主机端的一维数组中。根的索引为 0,根的子级的索引为 1-8,下一级的索引为 9-72,依此类推。八叉树共有 9 个级别(其中最后一个级别具有完整的 256x256x256 分辨率)。由于八叉树总是满的,因此结构是隐式的,并且不需要存储指针,只需存储每个体素的一个浮点值。我已经设置好了一维索引和遍历算法。
我的问题是我不知道如何将其存储在纹理中。 GL_TEXTURE_MAX_SIZE 对于使用我已经计算出索引的一维数组方法来说太小(16384)。我需要将其存储在 3D 纹理中,并且我不知道当我尝试将 1D 数组塞入其中时会发生什么,也不知道如何选择大小和 1D->3D 转换方案以免浪费空间或时间。
我的问题是,是否有人有一个好的策略来将整个八叉树结构存储在一个 3D 纹理中,以及在这种情况下如何为其选择维度和索引。
最佳答案
首先介绍一下如何直接移植一维数组解决方案:
首先,正如 Mortennobel 在他的评论中所说,最大纹理大小很可能不是 3397
,这只是 GL_MAX_TEXTURE_SIZE
的枚举值(定义该值的 opengl.h header 应该如何了解您的硬件和驱动程序限制?)。要从您的实现中获取实际值,请使用 int size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
。但即便如此,这对你来说可能还是太小了(可能是 8192 或类似的东西)。
但是要将更大的一维数组放入着色器中,您可以使用 buffer textures (自 OpenGL 3 起就是核心,因此存在于 DX10 级硬件上)。这些是从标准 OpenGL 缓冲区对象获取数据的纹理。但这些纹理始终是一维的,通过整数 texCoords(可以说是数组索引)访问并且不进行过滤。因此,它们实际上并不是真正的纹理,而是一种在着色器内以线性一维数组形式访问缓冲区对象的方法,这非常适合您的需求(事实上,比普通的过滤和归一化一维纹理更适合) .
编辑:您可能还会考虑像以前一样使用直接的 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 数组布局,要么使用带有 mipmap 的 3D 纹理来进行直接的八叉树映射(或者可能提出一个完全不同且更复杂的数据结构)首先是体素网格)。
编辑:当然,完全分割的八叉树并没有真正利用八叉树的内存节省功能来发挥其优势。对于将八叉树打包到 3D 纹理中的更动态和内存效率更高的方法,您也可以从这个经典的 GPU Gems article on octree textures 中获得一些灵感。 。它们基本上将所有八叉树单元作为 2x2x2 网格任意存储到 3D 纹理中,使用内部节点的值作为指向该纹理中子级的指针。当然,现在您可以对此进行各种改进(因为您似乎也希望内部节点存储数据),例如将整数与 float 一起存储以及使用良好的位编码等,但基本思想非常简单。
关于opengl - 将数据打包到 OpenGL 3D 数组中的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15490627/