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

我用两个四元数来旋转。每次用户选择一个动作时,都会发生一个旋转。代码如下:
    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)
然后第一次逆时针旋转x 90度(CCW)产生
X’-右(1,0,0)
Y'-英寸(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-英寸(0,0,1)
Z-右(1,0,0)
我认为问题在于resultvector(我使用的第二个从原点指向视点的向量)转换不正确。有人知道如何将世界坐标转换为对象坐标吗?有人知道当物体旋转时我们如何确定物体的坐标吗?

最佳答案

昨天我决定编写RubicCube拼图,因为我过去尝试过的任何一个拼图都让我很不舒服,最后我有心情/时间自己编写。当我完成它时,这里已经是我的见解:
rubic立方表示
我不认为四元数是一个很好的选择。相反,我更喜欢:
4x4 uniform transform matrices
所以我得到了一个3*3*3=27变换矩阵的列表,外加一个用于整个立方体旋转的附加矩阵。在开始状态下,所有子立方体都有单位旋转部分,原点设置为覆盖{ -1 , 0 ,+1 }的所有组合,以填充整个红宝石立方体(每个子立方体网格的大小1.0且以(0,0,0)为中心)。
android - 四元数旋转不起作用-LMLPHP
我的立方体在C++代码中定义如下:

reper cube[27]; // reper is transform matrix

GUI
我想控制和观看尽可能接近真实的东西。因此,旋转由鼠标通过简单地单击目标子立方体(在area0area1中)来控制,然后从鼠标拖动的方向决定旋转的轴和方向。
从一开始就没有问题(因为即使你的代码也能很好地工作)。问题从下一个旋转开始(特别是在更改旋转轴时),因为本地坐标系已经更改。全局视图旋转也是如此,因为它会把这一切搞砸。
如何纠正局部坐标系的变化?
我想出了一个模糊的解决方案,首先从每个坐标系匹配轴。为了检测哪个轴,我只需做一个点积的查询方向与所有轴的转换矩阵,并选择一个最高的abs点积。符号只是告诉坐标系是否相反(意味着旋转应该相反)。
在C++和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;
    }

其中reper是包含直接和逆变换矩阵的类。get_axis只需查看直接矩阵内部并返回选定的轴方向单位向量。vector_mul是点积,vector_ld只是用x,y,z坐标填充3d向量。
由于我还得到了全局立方体矩阵,它的轴与单位矩阵不对齐(因为它是旋转的,所以视图看起来像上面的图像),所以我需要对特殊向量(初始视图矩阵值)进行轴匹配,在我的情况下是这样的:
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)。
切片旋转
这是拼图的核心。它简单的绕轴旋转切片。这是用来设置动画的,所以角度步是小的(我用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);
        }
    }

其中reper::gpos_get以3d向量(点)的形式返回矩阵原点,reper::gpos_set基本上设置了新的矩阵位置。vector_rotx(p0,p,a)旋转向量pp0旋转,轴x旋转角度a+/-符号只匹配来自reper类的旋转(我在某些地方得到了差异)。reper::lrotx绕其局部reper轴旋转x有关更多信息,请参见第一个链接。
如您所见,我使用每个矩阵原点坐标直接作为拓扑来选择切片立方体。
在这里您可以尝试我的演示:Win32+OpenGL Rubic Cube Demo
这里是一些转弯的动画gif:
android - 四元数旋转不起作用-LMLPHP
[Edit1]我在我的Rubicube中添加了简单的解算器
为了实现一个解算器,我添加了表面平面颜色贴图(在左边…中间的正方形是我使用的边的名称和索引)是从我的rubicube内部表示计算出来的。我还为解算器添加了内部命令que(右边的轴和方向):
android - 四元数旋转不起作用-LMLPHP
每个命令由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];

其中map[side][u][v]包含边s上的正方形、行u和列v的颜色。我实现了simple7 steps solution(就像用人求解真实的立方体):
android - 四元数旋转不起作用-LMLPHP
输入状态(不是步骤)
带黄色中间的白色十字(黄色中间面向前面)
白色十字(白色中间面向前面)
白角(白边朝下)
中间层(使用前3个命令)
顶层黄十字(使用第四个命令)
重新排序交叉边以便匹配(第5个命令)和重新排序角点(第6个命令)
将顶层角点定向到完整立方体(第7个命令)
解算器是简单的,操作字符串(未优化),所以它有点慢,但无论如何,完整的解决方案只需要50毫秒在我的设置。您可以在此处尝试升级的演示:
Win32+OpenGL Rubic Cube Demo with solver
在解决时,可能仍有一些未定义的情况(由于代码中的错误或遗漏的情况)。在这种情况下,应用程序挂起粗糙(尚未实现看门狗)。这些键在包含的文本文件中进行了描述。
我做了解算器轻量级(cca 300行代码),所以找到的解决方案远远不是最优的。例如,我只测试一个角点,然后循环旋转立方体,导致不必要的旋转。其中一些被过滤掉了,但是人类(我)的平均解是200圈,这个解算器返回300圈。

07-28 03:46
查看更多