四元数旋转无法正常工作

问题描述 投票:5回答:2

在用于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-in(0,0,1)

然后第一次逆时针旋转X 90度(CCW)产生

  • [X'-右(1,0,0)
  • Y'-in(0,0,1)
  • Z'-down(0,-1,0)

和绕Z轴逆时针旋转90度产生第二个

  • X'-in(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(我使用的第二个向量,它从原点到视点)无法正确转换。有人知道如何将世界坐标转换为对象坐标吗?有谁知道当物体旋转时我们如何确定物体坐标?

android opengl-es
2个回答
4
投票

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

  1. Rubic多维数据集表示形式>>

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

    • 4x4 uniform transform matrices
    • 所以我最终得到了3*3*3=27变换矩阵的列表以及整个多维数据集旋转的一个附加矩阵。在开始状态下,所有子立方体均具有单位旋转部分,并且其原点设置为覆盖{ -1 , 0 ,+1 }的所有组合以填充整个Rubic Cube(每个子立方体网格的大小为1.0,并以(0,0,0)为中心)] >

      axises

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

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

  3. 我希望控制和查看尽可能地接近真实的事物。因此,可以通过鼠标单击目标子多维数据集(在area0area1中)来控制旋转,然后从鼠标拖动的方向确定旋转的轴和旋转的方向。

    从开始位置没有问题(因为即使您的代码也能很好地工作)。问题开始于下一个旋转(尤其是在更改旋转轴时),因为局部坐标系已经更改。全局视图旋转同样如此,因为它将使所有这些混乱。

  4. 如何解决改变局部坐标系?

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

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

  6. 切片旋转

  7. 这是难题的核心。它简单地绕轴旋转切片。这是用来制作动画的,因此角度步长较小(我使用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动画:

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

[map[side][u][v]包含侧面s,行u和列v的正方形的颜色。我实现了简单的7 steps solution(例如通过人工求解实际的多维数据集):

solution steps

    输入状态(不是步骤)
  1. 带有黄色中间的白色十字(黄色中间朝前)
  2. 白色十字(白色中间朝向前面)
  3. 白角(白边朝下)
  4. 中间层(使用前三个命令)
  5. 顶层黄色十字(使用第四个命令)
  6. 重新排列十字,使边匹配(第5个命令),对角进行重新排序(第6个命令)
  7. 定向顶层角以完成立方体(第七个命令)
  8. Solver很简单,可以对字符串进行操作(未优化),因此它有点慢,但无论如何,完整的解决方案在我的设置上最多需要50ms。您可以在这里尝试升级后的演示:
    Win32+OpenGL Rubic Cube Demo with solver
  • 在解决时,可能仍然存在一些未定义的情况(由于错误或代码遗漏情况)。在这种情况下,应用程序挂起了粗略的挂起(尚未实现看门狗)。键在附带的文本文件中描述。
  • 我做了轻量级的求解器(大约300行代码),所以找到的解决方案远非最佳。例如,代替测试4个角,我仅测试一个角,然后循环旋转立方体,从而导致不必要的转弯。它们中的一些后来被过滤掉了,但是平均人类(我的)解决方案最多旋转200转,而该求解器则返回最多300转(最糟糕的情况是直到现在我仍然发现)。

发生的情况是,当将此变换应用于模型(在您的情况下为旋转)时,也会旋转它的基本向量。可以将其视为好像您也将旋转坐标系一样,或者就像是从模型的第一人称视角看一样。您进行的每次转换都会影响下一个转换。

由于您通常希望保留自己的坐标系,因此您可能需要考虑在立方体周围移动相机,而不是旋转立方体。我相信您可以在API或网络上找到“ lookAt”方法。它应该采用3个向量:cameraPosition,lookAtPoint,upVector。通过这种方法,您可以将多维数据集定位到(0,0,0),这也是您的“ lookAtPoint”,第一个cameraPosition应该类似于(0,0,-1),第一个upVector应当等于(0,1,0)。现在开始运动(您可能只使用左/右和上/下作为输入):要向上/向下(围绕X旋转),您需要执行以下操作:

originalDistance = (cameraPosition-objectPosition).lenght leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition camearPosition = cameraPosition + upVector*inputScalar //inputScalar should be a small floating value cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition

要向左/向右(围绕X旋转),请执行以下操作:

originalDistance = (cameraPosition-objectPosition).lenght leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition camearPosition = cameraPosition + leftVector*inputScalar //inputScalar should be a small floating value cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object leftVector = normalizedVector(crossProduct(cameraPosition, upVector))//generaly cameraPosition-objectPosition upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition

这通常应该可以解决问题。.(请告诉我我在努力编写时是否犯了一个错误)

关于旋转对象本身的方法,应该在对象自己的坐标系中找出四元数,然后围绕该四元数旋转它。如果您掌握一些数学技能,这也很容易。除此之外,您还可以只定义2个角度(X,Y)并通过输入直接更改它们,并使用(1,0,0,X)和(0,1,0,Y)的四元数,但是可能存在问题当Y为90度时采用这种方法。

我希望这会有所帮助。


1
投票
发生的情况是,当将此变换应用于模型(在您的情况下为旋转)时,也会旋转它的基本向量。可以将其视为好像您也将旋转坐标系一样,或者就像是从模型的第一人称视角看一样。您进行的每次转换都会影响下一个转换。
© www.soinside.com 2019 - 2024. All rights reserved.