非立方区域的 Morton 曲线是 2 的幂
Morton curve for non cubic areas that are a power of two
在优化光线追踪器时,我尝试使用 Morton space 填充曲线改进交叉数据结构的数据局部性,除了我的 3D space 不是立方体(例如 512x512x256)。所有边都是2的幂,但不是所有边都一样长。
我一直无法找到边为 2 的幂的非正方形 Morton 曲线的任何示例。如果重要的话,我可以保证 x/y 轴的大小相同,只是 z 轴的长度不同。
请注意宽度是 2x 高度,但它也可以是 3x 或 4x 或任何其他。我一直无法找到执行此操作的方法。
理想情况下,解决方案会很快,因为必须大量计算 Morton 代码。所以我的问题是:如何为非三次 spaces 生成 space 填充莫顿曲线?这是专门针对 GPU (Cuda) 的。
尺寸条件为:
x, y, z 是二的幂
x == y
x, y >= z
或者如果更容易
x, y > z
它可能会因为 nz=11 而中断,但是对于 XY 正方形的一半大小它似乎对我有用
#include <cstdint>
#include <iostream>
static inline uint32_t spread(uint32_t x)
{
x = (x | (x << 10)) & 0x000F801F;
x = (x | (x << 4)) & 0x00E181C3;
x = (x | (x << 2)) & 0x03248649;
x = (x | (x << 2)) & 0x09249249;
return x;
}
static inline uint32_t morton(const uint32_t x, const uint32_t y, const uint32_t z)
{
return spread(x) << 0 | spread(y) << 1 | spread(z) << 2;
}
auto main() -> int {
int nx = 32;
int ny = 32;
int nz = 16;
for (int iz = 0; iz != nz; ++iz)
{
for (int iy = 0; iy != ny; ++iy)
{
for (int ix = 0; ix != nx; ++ix)
{
auto m = morton(ix, iy, iz);
std::cout << m << '\n';
}
}
}
return 0;
}
更新
如何使 Morton 代码适用于 256x256x64(8 位*8 位*6 位):您必须非等距离地展开 X 和 Y,同时考虑 Z 中的位数。基本上,对于您展开的立方体均匀地:每一位都在位置上
0, 3, 6, 9, 12, 15, 18, 21, 24,
为来自正交轴的其他两位留下 space。
所以立方体是等距分布的。但是对于从 Z 插入只有 6 位的情况,您必须有 6 个 3 的距离,但最后一个间隙没有 Z 位,因此 X 和 Y 扩展的最后一个间隙应该只有 1 位宽。因此,X 和 Y 的非等距分布。
沿线的东西:如果 Nx=Ny 是 XY 平面中的位数,并且 Nz!=Nx 或 Ny 是沿 Z 轴的位数,
Nz 位的扩展间隙应为 2 位,剩余的间隙应为 1 位。所以有两个传播例程 - 一个用于具有非等距传播的 X&Y,现在取决于 Nz,以及用于 Z 轴的现有传播函数。
好的,这是一个工作版本,似乎做对了
#include <cstdint>
#include <iostream>
#define func auto
func spreadZ(uint32_t v) -> uint32_t { // 2bit gap spread
v = (v | (v << 10)) & 0x000F801F;
v = (v | (v << 4)) & 0x00E181C3;
v = (v | (v << 2)) & 0x03248649;
v = (v | (v << 2)) & 0x09249249;
return v;
}
func spreadXY(const uint32_t v, const uint32_t bitsZ) -> uint32_t {
uint32_t mask_z = (1U << bitsZ) - 1U; // to mask bits which are going to have 2bit gap
uint32_t lo{ v & mask_z }; // lower part of the value where there are Z bits
lo = spreadZ(lo); // 2bit gap spread
uint32_t hi = v >> bitsZ; // high part of the value, 1bit gap
// 1bit gap spread
hi = (hi ^ (hi << 8)) & 0x00ff00ffU;
hi = (hi ^ (hi << 4)) & 0x0f0f0f0fU;
hi = (hi ^ (hi << 2)) & 0x33333333U;
hi = (hi ^ (hi << 1)) & 0x55555555U;
return lo + (hi << 3*bitsZ); // combine them all together
}
func morton(const uint32_t x, const uint32_t y, const uint32_t z, const uint32_t bitsZ) -> uint32_t {
return spreadXY(x, bitsZ) << 0 | spreadXY(y, bitsZ) << 1 | spreadZ(z) << 2;
}
func ispowerof2(const uint32_t n) -> bool {
return n && (!(n & (n - 1u)));
}
func bit_pos(const uint32_t n) -> uint32_t {
if (!ispowerof2(n))
throw -1;
uint32_t i{ 1u }, pos{ 1u };
while (!(i & n)) { // Iterate through bits of n till we find a set bit, i&n will be non-zero only when 'i' and 'n' have a same bit
i = i << 1; // unset current bit and set the next bit in 'i'
++pos; // increment position
}
return pos;
}
func main() -> int {
int nx = 256;
int ny = 256;
int nz = 256; //256...128...64...32...16...8...4...2...1 all works
int bitsZ = bit_pos(nz) - 1; // should be doing try/catch
for (int iz = 0; iz != nz; ++iz)
{
for (int iy = 0; iy != ny; ++iy)
{
for (int ix = 0; ix != nx; ++ix)
{
auto m = morton(ix, iy, iz, bitsZ);
std::cout << m << '\n';
}
}
}
return 0;
}
在优化光线追踪器时,我尝试使用 Morton space 填充曲线改进交叉数据结构的数据局部性,除了我的 3D space 不是立方体(例如 512x512x256)。所有边都是2的幂,但不是所有边都一样长。
我一直无法找到边为 2 的幂的非正方形 Morton 曲线的任何示例。如果重要的话,我可以保证 x/y 轴的大小相同,只是 z 轴的长度不同。
请注意宽度是 2x 高度,但它也可以是 3x 或 4x 或任何其他。我一直无法找到执行此操作的方法。
理想情况下,解决方案会很快,因为必须大量计算 Morton 代码。所以我的问题是:如何为非三次 spaces 生成 space 填充莫顿曲线?这是专门针对 GPU (Cuda) 的。
尺寸条件为:
x, y, z 是二的幂
x == y
x, y >= z
或者如果更容易
x, y > z
它可能会因为 nz=11 而中断,但是对于 XY 正方形的一半大小它似乎对我有用
#include <cstdint>
#include <iostream>
static inline uint32_t spread(uint32_t x)
{
x = (x | (x << 10)) & 0x000F801F;
x = (x | (x << 4)) & 0x00E181C3;
x = (x | (x << 2)) & 0x03248649;
x = (x | (x << 2)) & 0x09249249;
return x;
}
static inline uint32_t morton(const uint32_t x, const uint32_t y, const uint32_t z)
{
return spread(x) << 0 | spread(y) << 1 | spread(z) << 2;
}
auto main() -> int {
int nx = 32;
int ny = 32;
int nz = 16;
for (int iz = 0; iz != nz; ++iz)
{
for (int iy = 0; iy != ny; ++iy)
{
for (int ix = 0; ix != nx; ++ix)
{
auto m = morton(ix, iy, iz);
std::cout << m << '\n';
}
}
}
return 0;
}
更新
如何使 Morton 代码适用于 256x256x64(8 位*8 位*6 位):您必须非等距离地展开 X 和 Y,同时考虑 Z 中的位数。基本上,对于您展开的立方体均匀地:每一位都在位置上 0, 3, 6, 9, 12, 15, 18, 21, 24, 为来自正交轴的其他两位留下 space。
所以立方体是等距分布的。但是对于从 Z 插入只有 6 位的情况,您必须有 6 个 3 的距离,但最后一个间隙没有 Z 位,因此 X 和 Y 扩展的最后一个间隙应该只有 1 位宽。因此,X 和 Y 的非等距分布。
沿线的东西:如果 Nx=Ny 是 XY 平面中的位数,并且 Nz!=Nx 或 Ny 是沿 Z 轴的位数, Nz 位的扩展间隙应为 2 位,剩余的间隙应为 1 位。所以有两个传播例程 - 一个用于具有非等距传播的 X&Y,现在取决于 Nz,以及用于 Z 轴的现有传播函数。
好的,这是一个工作版本,似乎做对了
#include <cstdint>
#include <iostream>
#define func auto
func spreadZ(uint32_t v) -> uint32_t { // 2bit gap spread
v = (v | (v << 10)) & 0x000F801F;
v = (v | (v << 4)) & 0x00E181C3;
v = (v | (v << 2)) & 0x03248649;
v = (v | (v << 2)) & 0x09249249;
return v;
}
func spreadXY(const uint32_t v, const uint32_t bitsZ) -> uint32_t {
uint32_t mask_z = (1U << bitsZ) - 1U; // to mask bits which are going to have 2bit gap
uint32_t lo{ v & mask_z }; // lower part of the value where there are Z bits
lo = spreadZ(lo); // 2bit gap spread
uint32_t hi = v >> bitsZ; // high part of the value, 1bit gap
// 1bit gap spread
hi = (hi ^ (hi << 8)) & 0x00ff00ffU;
hi = (hi ^ (hi << 4)) & 0x0f0f0f0fU;
hi = (hi ^ (hi << 2)) & 0x33333333U;
hi = (hi ^ (hi << 1)) & 0x55555555U;
return lo + (hi << 3*bitsZ); // combine them all together
}
func morton(const uint32_t x, const uint32_t y, const uint32_t z, const uint32_t bitsZ) -> uint32_t {
return spreadXY(x, bitsZ) << 0 | spreadXY(y, bitsZ) << 1 | spreadZ(z) << 2;
}
func ispowerof2(const uint32_t n) -> bool {
return n && (!(n & (n - 1u)));
}
func bit_pos(const uint32_t n) -> uint32_t {
if (!ispowerof2(n))
throw -1;
uint32_t i{ 1u }, pos{ 1u };
while (!(i & n)) { // Iterate through bits of n till we find a set bit, i&n will be non-zero only when 'i' and 'n' have a same bit
i = i << 1; // unset current bit and set the next bit in 'i'
++pos; // increment position
}
return pos;
}
func main() -> int {
int nx = 256;
int ny = 256;
int nz = 256; //256...128...64...32...16...8...4...2...1 all works
int bitsZ = bit_pos(nz) - 1; // should be doing try/catch
for (int iz = 0; iz != nz; ++iz)
{
for (int iy = 0; iy != ny; ++iy)
{
for (int ix = 0; ix != nx; ++ix)
{
auto m = morton(ix, iy, iz, bitsZ);
std::cout << m << '\n';
}
}
}
return 0;
}