四元数旋转无法正常工作 [英] Quaternion rotation does not work as excepted

查看:94
本文介绍了四元数旋转无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In OpenGL ES 1 for android, I have a Rubic cube that consists of 27 smaller cubes. i want rotations which cause particular small cube becoming exactly in front of the viewpoint. so I need two vectors. one is the vector that comes from the origin of the object to a particular cube. and another is the vector that comes from origin to the viewpoint. then the cross product of them gives me the axis of the rotation and the dot product gives me the angle.

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

I convert the (0,0,1) -which is the vector that comes from the origin to the viewpoint in world coordinate- to object coordinates. here is the code:

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

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

I use two Quaternions for rotations. each time user choose an action one of that rotations should happen. here is the code :

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

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

now the problem is that it just works fine for the first rotation. whatever the first rotation would be. it works well but for the next rotations it seems that it gets wrong axis and degree.

例如,如果坐标系为

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

然后逆时针(CCW)绕X 90度进行第一次旋转

then first rotation around X 90 degrees counter clockwise (CCW) produces

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

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

and second rotation around Z 90 degrees CCW produces

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

但我希望

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

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

I think the problem is that the resultVector(the second vector which I used that comes from origin toward the viewpoint) does not convert properly. anyone knows how can I convert the world coordinate to object coordinate? anyone knows how can we determine object coordinates when object have rotated?

推荐答案

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

Well yesterday I decided to code Rubic Cube puzzle because any I tried in past was really uncomfortable for me and finally got some mood/time to code it myself. As I finished it already here are mine insights:

  1. Rubic Cube表示形式

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

I do not see quaternions as a good choice for this. Instead I am more comfortable with:

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

So I ended up with list of 3*3*3=27 transform matrices plus one additional for whole cube rotations. In starting state all the sub cubes have unit rotation part and the origins are set to cover all the combinations of { -1 , 0 ,+1 } to fill the whole Rubic Cube (size of each sub cube mesh is 1.0 and centered around (0,0,0))

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

My cubes are in C++ code defines like this:

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

  • GUI

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

    I wanted the controlling and viewing to be as close to the real thing as I could. So the rotations are controlled by mouse by simple click on target sub-cube (in area0 or area1) and then from direction of mouse drag it is decided which axis is rotated and in which direction.

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

    From starting position there is no problem (as even your code works well for that). The problems start on next rotation (especially when changing axis of rotation) because the local coordinate systems already changed. The same goes for global view rotation as it will mess up all of this.

    如何解决更改局部坐标系的问题?

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

    I come up with a obscure solution where I first match axis from each coordinate system. To detect which axis is which I simply do a dot product of queried direction vs. all axises of the transform matrix and choose the one with highest abs dot product. The sign just tells if the coordinate system is opposite (meaning the rotation should be reversed).

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

    In C++ and OpenGL style matrices it looks like this:

    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向量.

    Where reper is class containing direct and invers transform matrix. the get_axis just peek inside direct matrix and return selected axis direction unit vector. The vector_mul is dot product and vector_ld just fills 3D vector with x,y,z coordinates.

    由于我还得到了一个不与单位矩阵轴向对齐的全局立方体矩阵(由于旋转,所以视图看起来如上图所示),然后我需要针对特殊矢量(初始视图矩阵值)进行此轴匹配就我而言是这样的:

    As I got also the global cube matrix which is not axis aligned to unit matrix (as it is rotated so the view looks like the image above) Then I need to do this axis matching against special vectors (initial view matrix values) In my case it is this:

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

    Both functions return which axis is which x,y,z and if the direction is opposite (sx,sy,sz) in comparison to the unit transform matrix.

    切片旋转

    这是难题的核心.它简单地绕轴旋转切片.这是用来进行动画处理的,因此角度步长较小(我使用9度),但整个转弯必须总共为90度,否则Rubic Cube会破裂.

    This is the core of the puzzle. It simple turn of slice around axis. This is used to animate so the angle step is small (I use 9 degree) but the whole turn must be 90 degree total otherwise the Rubic Cube would break.

    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)将矢量p围绕p0和轴x旋转角度a. +/-符号仅与reper类的旋转匹配(我在某处有所不同). reper::lrotx绕其本地x轴旋转reper有关更多信息,请参阅第一个链接.

    Where reper::gpos_get returns matrix origin as 3D vector (point) and reper::gpos_set basically sets new matrix position. The vector_rotx(p0,p,a) rotates vector p around p0 and axis x by angle a. The +/- signs are only to match the rotations from reper class (I got difference somewhere). The reper::lrotx rotates reper around its local x axis for more info see the first link.

    如您所见,我直接将每个矩阵原点坐标用作拓扑来选择切片多维数据集.

    As you can see I am using each matrix origin coordinates directly as topology to select the slice cubes.

    在这里您可以尝试我的演示: Win32 + OpenGL Rubic多维数据集演示

    下面是一些转弯的gif动画:

    And Here animated gif of some turns:

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

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

    To implement a solver I added surface planar color map (on the left ... the middle square is name and index of side I use) computed from my RubiCube internal representation. I also add internal commands que for the solver (axises and direction on the right):

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

    Each commands is represented by 2 character strings:

    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步解决方案(例如,通过人工求解实际的多维数据集):

    Where map[side][u][v] contains color of square on side s, row u and column v. I implemented simple 7 steps solution (like solving real cube by human):

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

    Solver很简单,并且可以对字符串进行操作(未优化),因此它有点慢,但是无论如何,完整的解决方案在我的设置上最多需要50ms.您可以在此处尝试升级后的演示:

    Solver is simple and operates on strings (unoptimized) so its a bit slow but anyway full solution takes just up to 50ms on my setup. You can try here the upgraded demo:

    在解决过程中,可能仍然存在一些未定义的情况(由于错误或代码中遗漏了情况).在这种情况下,应用程序挂起了粗略的挂起(尚未实现看门狗).密钥在随附的文本文件中描述.

    There may still be some undefined cases (due to bug or missed case in code) while solving. In such case the app hangs of coarse (did not implement watchdog yet). The keys are described in the text file included.

    我做了轻量级的求解器(大约300行代码),所以找到的解决方案远非最优.例如,代替测试4个角,我仅测试一个角,然后循环旋转立方体,从而导致不必要的转弯.它们中的一些后来被过滤掉了,但是平均人类(我的)解决方案最多旋转200转,而该求解器返回最多300转(在最糟糕的情况下,我发现到现在为止).

    I did the solver lightweight (cca 300 lines of code) so the found solution is far from optimal. For example instead testing 4 corners I test only one and rotate the cube in loop causing unnecessary turns. Some of them are filtered out latter but the average human (my) solution is up to 200 turns and this solver return up to 300 turns instead (in worst case I found till now).

    这篇关于四元数旋转无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆