Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/159.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何有效地计算将单位立方体映射到自身的反射和旋转?_C++_Sse_Affinetransform_Isomorphism - Fatal编程技术网

C++ 如何有效地计算将单位立方体映射到自身的反射和旋转?

C++ 如何有效地计算将单位立方体映射到自身的反射和旋转?,c++,sse,affinetransform,isomorphism,C++,Sse,Affinetransform,Isomorphism,对于基于八叉树的稀疏体素八叉树渲染器,我希望能够旋转和镜像八叉树的各个节点。虽然它不是一个真正的八叉树,但由于节点被共享,并且被允许作为子树包含自己。否则,我可以简单地将转换应用到八叉树本身 在不丧失一般性的情况下,假设立方体是一个单位立方体,其一个角位于原点,即(0,0,0),另一个角位于(1,1,1)。我将立方体的角点编码为3位整数。因此,0(=0b000)表示原点处的角,1(=0b001)表示(0,0,1)处的角,7(=0b111)表示(1,1,1)处的角。唯一允许的旋转和反射是围绕立方体

对于基于八叉树的稀疏体素八叉树渲染器,我希望能够旋转和镜像八叉树的各个节点。虽然它不是一个真正的八叉树,但由于节点被共享,并且被允许作为子树包含自己。否则,我可以简单地将转换应用到八叉树本身

在不丧失一般性的情况下,假设立方体是一个单位立方体,其一个角位于原点,即(0,0,0),另一个角位于(1,1,1)。我将立方体的角点编码为3位整数。因此,0(=0b000)表示原点处的角,1(=0b001)表示(0,0,1)处的角,7(=0b111)表示(1,1,1)处的角。唯一允许的旋转和反射是围绕立方体的中心(½,½,½)进行的,因此角点将以整数坐标结束。这意味着只有48种不同的可能转换。(第一个角点可以映射到8个可能的位置,下一个角点可以映射到3,第三个角点可以映射到2,这将固定其余角点的位置。)

我还没有决定如何对转换进行编码,不过应该可以将其编码为单个32位整数(甚至6位整数,因为只有48种可能的转换)。然后可以将转换应用为函数,将立方体的每个角映射到转换后的位置。即

int变换(int角点,int变换){
//魔法在这里发生
返回结果;
}
此外,我还应该有一个组合变换的函数
combine
,这样
transform(corner,combine(a,b))
等于
transform(transform(corner,b),a)

由于这些函数每秒将被调用10亿次,它们应该很快。尽管transform的调用频率大约是combine的4倍。该算法是递归的,因此如果转换编码使用的是多个32位整数,则将其放入堆栈会产生额外的运行时成本

到目前为止,我已经发现这个问题可以分解为位翻转(可以通过单个异或操作完成)和位置换(我还不知道如何有效地完成)。不过,同时执行这两项操作可能更有效

我打算在C++代码中使用这个代码,它已经使用了SSE4.1内核。尽管首选不使用内部函数或仅需要SSE3的解决方案。最后,速度是最重要的。我将尝试使用来比较解决方案


(注意:我使用了affinetransform标记,因为没有正交变换标记,我不想创建它)。

您可以将变换存储为一个8字节的数组,该数组对立方体角点的排列进行编码。
transform
方法与数组查找一样简单。组合这两个转换可以在一个循环中完成,但这将相当缓慢。但是,SSSE3有一个内在函数,它在一条指令中实现了这一点:

因此,必要的方法可按如下方式实施:

#include <tmmintrin.h>

int transform(int corner, __m64 transformation) {
    uint8_t array[8];
    memcpy(array, &transformation, 8); // This call is optimized away by the compiler.
    return array[corner];
}

__m64 combine(T a, T b) {
    return _mm_shuffle_pi8(b, a);
}
#包括
int变换(int角点,_m64变换){
uint8_t数组[8];
memcpy(array,&transformation,8);//此调用由编译器进行优化。
返回数组[角点];
}
__m64联合收割机(T a、T b){
return-mm-shuffle-pi8(b,a);
}
从我的基准测试结果来看,调用转换四次,合并一次,大约需要2秒的CPU时间。这仍然有点慢,但有足够的内核,这可能是可行的


我还实现了一些不同的比较方法:

  • 将置换打包为int32中的半字节(4位整数),并使用循环计算组合的结果。虽然使用了一半的内存,但速度要慢3-5倍
  • 直接研究Milo Brandt提出的6位编码。然而,我未能正确地实现这一点
  • 将允许的变换限制为仅轴对齐反射的组合。这些可以使用逐位异或快速计算。这大约快30-50%

我创建了一个,尽管它不支持使用SSSE3指令,所以我不得不禁用使用
\u mm\u shuffle\u pi8
的方法。您仍然可以在本地编译和运行它们。

您可以将8个新角点连接起来,得到一个24位整数。然后我可以用一个简单的bitshift+掩码实现转换,速度非常快。但是我该如何实现合并功能呢?如果您有两个转换
a
b
,使得
a_I
I
-th(were
I
只是由角点的二进制表示形式表示的自然数)角点的图像,你可以通过设置
c|i=b|a|i |}
来计算它们的组合
c
,其中
|a|i |
是由二进制字符串
a|i
表示的自然数。有一个很好的6位编码:首先将对称分解为它在坐标平面(x,y,z)上的行为(六种可能性)加上它发送角的位置(1,1,1)。根据移位索引是否以正确的循环顺序出现,将(x,y,z)的排列分解为一个符号位,然后再分解两个位来描述x的去向。(对于正方形的对称性,类似的技巧也能很好地发挥作用……我不确定如何有效地处理三个元素的排列-可能只是使用表格查找?
\uu m64
-通常你不想使用MMX。使用
\uuum128i
向量的低位8字节同样快(或更快),它可以进入XMM寄存器
\u mm\u storel\u epi64
\u mm\u loadl\u epi64
movq
存储/加载。此外,它看起来像是
transform
从向量元素中提取一个字节,然后将其扩展回零