四元数旋转不起作用

2023-11-26

在适用于 Android 的 OpenGL ES 1 中,我有一个由 27 个较小的立方体组成的 Rubic 立方体。我想要旋转,导致特定的小立方体恰好位于视点前面。所以我需要两个向量。一个是从对象原点到特定立方体的向量。另一个是从原点到视点的向量。然后它们的叉积给出了旋转轴,点积给出了角度。

我将 (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))));

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

    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);

现在的问题是它在第一次旋转时工作得很好。无论第一次旋转是什么。它工作得很好,但对于下一次旋转,它似乎得到了错误的轴和度数。

例如,如果坐标系是

  • X-右 (1,0,0)
  • Y 向上 (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'-输入 (0,1,0)
  • Y'-左 (-1,0,0)
  • Z'-向下 (0,-1,0)

但我期望

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

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


昨天,我决定编写 Rubic Cube 谜题,因为我过去尝试过的任何谜题对我来说都非常不舒服,最后我终于有了一些心情/时间自己编写代码。我已经完成了,这里是我的见解:

  1. Rubic 立方体表示

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

    • 4x4 均匀变换矩阵

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

    axises

    我的立方体在 C++ 代码中定义如下:

    reper cube[27]; // reper is transform matrix
    
  2. GUI

    我希望控制和观看尽可能接近真实的东西。因此,旋转是由鼠标控制的,只需单击目标子立方体(在area0 or area1),然后根据鼠标拖动的方向决定旋转哪个轴以及朝哪个方向旋转。

    从起始位置开始没有问题(因为即使你的代码也能很好地工作)。问题在下一次旋转时开始(尤其是在更改旋转轴时),因为局部坐标系已经更改。全局视图旋转也是如此,因为它会搞乱所有这一切。

  3. 如何纠正改变局部坐标系?

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

    In C++ and OpenGL样式矩阵看起来像这样:

    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;
        }
    

    Where reper是包含直接变换矩阵和逆变换矩阵的类。这get_axis只需查看直接矩阵并返回选定的轴方向单位向量即可。这vector_mul是点积并且vector_ld只需将 3D 向量填充为x,y,z坐标。

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

    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;
        }
    

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

  4. 切片旋转

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

    void RubiCube::cube_rotate(int axis,int slice,double ang)
        {
        int j,k,a[3],s[3];
        double p[3],p0[3]={0.0,0.0,0.0},lang;
        reper *r;
        _redraw=true;
        for (k=0;k<27;k++)
            {
            r=&cube[k];
            // local axis,sign
            axises_unit(*r,a[0],a[1],a[2],s[0],s[1],s[2]);
            // lang is local signed angle change
            lang=ang; if (s[axis]<0) lang=-lang;
            // select slice
            r->gpos_get(p);
            j=round(p[axis]+1.0);
            if (j!=slice) continue;
            // rotate global position
            if (axis==0) vector_rotx(p0,p,+ang);
            if (axis==1) vector_roty(p0,p,-ang);
            if (axis==2) vector_rotz(p0,p,+ang);
            r->gpos_set(p);
            // rotate local cube orientation
            if (a[axis]==0) r->lrotx(-lang);
            if (a[axis]==1) r->lroty(-lang);
            if (a[axis]==2) r->lrotz(-lang);
            }
        }
    

    Where reper::gpos_get返回矩阵原点作为 3D 向量(点)并且reper::gpos_set基本上设置新的矩阵位置。这vector_rotx(p0,p,a)旋转向量p around p0和轴x按角度a. The +/-标志只是为了匹配旋转reper类(我在某处有所不同)。这reper::lrotx旋转reper围绕其当地x轴有关更多信息,请参阅第一个链接。

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

在这里你可以尝试我的演示: Win32+OpenGL魔方演示

这是一些回合的动画 gif:

animation

[Edit1] 我向 RubiCube 添加了简单的解算器

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

axises

每个命令由 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

地图如下所示:

int map[6][3][3];

Where map[side][u][v]包含侧面正方形的颜色s, row u和列v。我实现了简单的7步解决方案(就像人类解决真正的立方体一样):

solution steps

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

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

  • Win32+OpenGL 魔方演示(带解算器)

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

我使用了轻量级求解器(cca 300 行代码),因此找到的解决方案远非最佳。例如,我只测试一个角,而不是测试 4 个角,并循环旋转立方体,导致不必要的转动。其中一些后来被过滤掉,但人类(我的)平均解决方案最多 200 圈,而该解算器返回最多 300 圈(到目前为止我发现的最坏情况)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

四元数旋转不起作用 的相关文章

随机推荐