四元数旋转无法正常运行

Ram*_*edi 5 android opengl-es

在适用于Android的OpenGL ES 1中,我有一个Rubic多维数据集,其中包含27个较小的多维数据集。我想要旋转,使特定的小立方体正好位于视点的前面。所以我需要两个向量 一个是从对象原点到特定立方体的向量。另一个是从原点到视点的向量。然后它们的叉积给我旋转轴,点积给我角度。

我将(0,0,1)(这是从原点到世界坐标中的视点的向量)转换为对象坐标。这是代码:

    matrixGrabber.getCurrentModelView(gl);
    temporaryMatrix.set(matrixGrabber.mModelView);

    inputVector[0] = 0f; 
    inputVector[1] = 0f;
    inputVector[2] = 1f;
    inputVector[3] = 1f;
    Matrix.multiplyMV(resultVector, 0, temporaryMatrix.InvertMatrix(), 0, inputVector,0);
    resultVector[0]/=resultVector[3];
    resultVector[1]/=resultVector[3];
    resultVector[2]/=resultVector[3];

    inputVector = ..... // appropriate vector due to user-selection 

    axis = Vector.normalized(Vector.crossProduct(Vector.normalized(inputVector), Vector.normalized(resultVector)));
    degree = (float)Math.toDegrees(Math.acos(Vector.dot(Vector.normalized(inputVector), Vector.normalized(resultVector))));
Run Code Online (Sandbox Code Playgroud)

我使用两个四元数进行旋转。每次用户选择一个动作时,都会发生一种轮换。这是代码:

    Quaternion currentRotation = new Quaternion();
    Quaternion temporaryRotation = new Quaternion();
    .
    .
    .
     currentRotation = (currentRotation).mulLeft(temporaryRotation.set(axis, degree));
     currentRotation.toMatrix(matrix);
     gl.glMatrixMode(GL10.GL_MODELVIEW);
     gl.glMultMatrixf(matrix, 0);
Run Code Online (Sandbox Code Playgroud)

现在的问题是,它在第​​一次旋转时效果很好。不管第一次旋转。它运作良好,但是对于下一个旋转,它似乎得到了错误的轴和度。

例如,如果坐标系为

  • X右(1,0,0)
  • 向上(0,1,0)
  • Z轴(0,0,1)

然后首先绕逆时针(CCW)旋转X 90度产生

  • X'-右(1,0,0)
  • Y'-in(0,0,1)
  • Z'-向下(0,-1,0)

第二次绕Z 90度逆时针旋转产生

  • X'-in(0,1,0)
  • 向左Y'(-1,0,0)
  • Z'-向下(0,-1,0)

但我期望

  • X向上(0,1,0)
  • Y英寸(0,0,1)
  • Z向右(1,0,0)

我认为问题在于resultVector(我使用的第二个向量,它是从原点到视点的)不能正确转换。有人知道如何将世界坐标转换为对象坐标吗?有谁知道当物体旋转时我们如何确定物体坐标?

Spe*_*tre 5

好吧,昨天我决定编写 Rubic Cube 拼图,因为我过去尝试过的任何拼图对我来说都非常不舒服,最后我有一些心情/时间自己编写代码。当我完成它时,这里是我的见解:

  1. 魔方表示

    我不认为四元数是一个好的选择。相反,我更喜欢:

    所以我最终得到了3*3*3=27变换矩阵列表加上一个用于整个立方体旋转的附加矩阵。在开始状态下,所有子立方体都有单位旋转部分,原点设置为覆盖 的所有组合{ -1 , 0 ,+1 }以填充整个 Rubic Cube(每个子立方体网格的大小为1.0并以 为中心(0,0,0)

    轴

    我的多维数据集在 C++ 代码中定义如下:

    reper cube[27]; // reper is transform matrix
    
    Run Code Online (Sandbox Code Playgroud)
  2. 图形用户界面

    我希望控制和查看尽可能接近真实的东西。所以旋转是由鼠标通过简单点击目标子立方体(inarea0area1)来控制的,然后从鼠标拖动的方向决定哪个轴旋转以及向哪个方向旋转。

    从起始位置没有问题(因为即使您的代码也适用于此)。问题从下一次旋转开始(尤其是在改变旋转轴时),因为局部坐标系已经改变。全局视图旋转也是如此,因为它会弄乱所有这些。

  3. 如何补救不断变化的局部坐标系?

    我想出了一个模糊的解决方案,我首先从每个坐标系匹配轴。为了检测哪个轴是哪个轴,我只需对查询方向与变换矩阵的所有轴进行点积,然后选择绝对点积最高的那个轴。该符号仅说明坐标系是否相反(意味着应该反转旋转)。

    C++OpenGL样式矩阵中,它看起来像这样:

    reper cube[27]; // reper is transform matrix
    
    Run Code Online (Sandbox Code Playgroud)

    reper包含直接和逆变换矩阵的类在哪里。的get_axis直接矩阵和返回选择的轴方向的单位矢量内部只是偷看。这vector_mul是点积,vector_ld只是用x,y,z坐标填充 3D 矢量。

    因为我也得到了全局立方体矩阵,它不是与单位矩阵对齐的(因为它被旋转,所以视图看起来像上图)然后我需要对特殊向量(初始视图矩阵值)进行这个轴匹配在我的情况下就是这个:

    void RubiCube::axises_unit(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,1.0,0.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,0.0,1.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,0.0,0.0,1.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    
    Run Code Online (Sandbox Code Playgroud)

    x,y,z与单位变换矩阵相比,这两个函数都返回哪个轴是哪个轴以及方向是否相反 (sx,sy,sz)。

  4. 切片旋转

    这是拼图的核心。它简单地绕轴旋转切片。这用于动画,因此角度步长很小(我使用 9 度),但整个转弯必须总共 90 度,否则魔方会破裂。

    void RubiCube::axises_obj(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,+0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,-0.000,-0.906,+0.423); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,-0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    
    Run Code Online (Sandbox Code Playgroud)

    哪里reper::gpos_get将矩阵原点作为 3D 向量(点)返回,并reper::gpos_set基本上设置新的矩阵位置。该vector_rotx(p0,p,a)旋转矢量p围绕p0和轴x由角度a。这些+/-标志只是为了匹配reper课堂上的轮换(我在某处有所不同)。该reper::lrotx旋转reper围绕其局部x轴的更多信息请参见第一个链接。

    如您所见,我直接使用每个矩阵原点坐标作为拓扑来选择切片立方体。

在这里你可以试试我的演示: Win32+OpenGL Rubic Cube Demo

这里是一些转弯的动画 gif:

动画片

[Edit1] 我在我的 RubiCube 中添加了简单的求解器

为了实现求解器,我添加了根据我的RubiCube内部表示计算的表面平面颜色贴图(在左侧......中间的方块是我使用的边的名称和索引)。我还为求解器添加了内部命令 que(右侧的轴和方向):

轴

每个命令由 2 个字符串表示:

edge slice  CW: R L U D F B
edge slice CCW: R'L'U'D'F'B'
mid  slice  CW: R0L0U0D0F0B0
whole cube  CW: RcLcUcDcFcBc
Run Code Online (Sandbox Code Playgroud)

地图如下所示:

int map[6][3][3];
Run Code Online (Sandbox Code Playgroud)

其中map[side][u][v]s、行u和列包含正方形的颜色v。我实现了简单的7 步解决方案(比如人工求解真正的立方体):

解决步骤

  1. 输入状态(不是一个步骤)
  2. 白十字黄中(黄中朝前)
  3. 白色十字(白色中间朝前)
  4. 白色边角(白色面朝下)
  5. 中间层(使用前 3 个命令)
  6. 顶层黄色十字(使用第 4 个命令)
  7. 重新排序交叉使双方匹配(第 5 个命令)和重新排序角(第 6 个命令)
  8. 定向顶层角以完成立方体(第 7 个命令)

求解器很简单,对字符串进行操作(未优化),所以它有点慢,但无论如何,完整的解决方案在我的设置中最多需要 50 毫秒。您可以在这里尝试升级的演示:

解决时可能仍然存在一些未定义的情况(由于代码中的错误或遗漏情况)。在这种情况下,应用程序会很粗糙(尚未实现看门狗)。密钥在包含的文本文件中进行了描述。

我做了轻量级的求解器(大约 300 行代码),所以找到的解决方案远非最佳。例如,代替测试 4 个角,我只测试一个角并循环旋转立方体,导致不必要的转弯。其中一些后来被过滤掉了,但人类(我的)解决方案的平均次数高达 200 圈,而这个求解器却返回了 300 圈(在最坏的情况下,我直到现在才发现)。