四元数旋转不起作用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四元数旋转不起作用相关的知识,希望对你有一定的参考价值。

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-up(0,1,0)
  • Z-in(0,0,1)

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

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

第二次旋转绕Z 90度CCW产生

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

但我希望

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

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

答案

好吧昨天我决定编写Rubic Cube拼图,因为我过去尝试过的任何东西对我来说真的很不舒服,终于有了一些心情/时间来自己编写代码。我完成它已经在这里是我的见解:

  1. Rubic Cube表示 我不认为四元数是一个很好的选择。相反,我更舒服: 4x4 uniform transform matrices 所以我最终得到了3*3*3=27变换矩阵列表以及一个额外的整个立方体旋转列表。在开始状态中,所有子立方体都具有单位旋转部分,并且原点设置为覆盖{ -1 , 0 ,+1 }的所有组合以填充整个Rubic立方体(每个子立方体网格的大小是1.0并且以(0,0,0)为中心) axises 我的多维数据集在C ++代码中定义如下: reper cube[27]; // reper is transform matrix
  2. GUI 我希望控制和观看尽可能接近真实的东西。因此,通过简单地点击目标子立方体(在area0area1中)来控制旋转,然后从鼠标拖动的方向决定旋转哪个轴以及在哪个方向上旋转。 从起始位置开始没有问题(因为即使你的代码也适用于此)。问题从下一次旋转开始(特别是在更改旋转轴时),因为局部坐标系已经改变。全局视图轮换也是如此,因为它会搞砸所有这些。
  3. 如何补救改变局部坐标系? 我提出了一个模糊的解决方案,我首先匹配每个坐标系的轴。为了检测哪个轴我只是查询方向的点积与变换矩阵的所有轴,并选择具有最高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)。
  4. 切片旋转 这是这个难题的核心。它绕轴切割简单。这用于动画,所以角度步长很小(我使用9度),但整个转弯必须是90度,否则Rubic Cube会破裂。 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围绕其当地的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. 中间层(使用前3个命令)
  6. 顶层黄色交叉(使用第4个命令)
  7. 重新排序交叉所以双方匹配(第5命令)和重新排序角落(第6命令)
  8. 定位顶层角落以完成立方体(第7个命令)

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

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

我做了轻量级求解器(cca 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

这通常应该解决问题..(请告诉我,如果我写错了,因为我写的很难)

至于旋转对象本身的方法,你应该找出对象自己的坐标系中的四元数是什么,并围绕那个坐标系旋转。如果你有一些数学技能,这也很

以上是关于四元数旋转不起作用的主要内容,如果未能解决你的问题,请参考以下文章

四元数quaternion

四元数和归一化

如何理解glm中四元数与向量相乘函数的写法

最短路径的四元数 slerp 不起作用

将四元数旋转转换为旋转矩阵?

两个四元数旋转的点积