四元数旋转不起作用
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拼图,因为我过去尝试过的任何东西对我来说真的很不舒服,终于有了一些心情/时间来自己编写代码。我完成它已经在这里是我的见解:
- Rubic Cube表示
我不认为四元数是一个很好的选择。相反,我更舒服:
4x4 uniform transform matrices
所以我最终得到了
3*3*3=27
变换矩阵列表以及一个额外的整个立方体旋转列表。在开始状态中,所有子立方体都具有单位旋转部分,并且原点设置为覆盖{ -1 , 0 ,+1 }
的所有组合以填充整个Rubic立方体(每个子立方体网格的大小是1.0
并且以(0,0,0)
为中心) 我的多维数据集在C ++代码中定义如下:reper cube[27]; // reper is transform matrix
- GUI
我希望控制和观看尽可能接近真实的东西。因此,通过简单地点击目标子立方体(在
area0
或area1
中)来控制旋转,然后从鼠标拖动的方向决定旋转哪个轴以及在哪个方向上旋转。 从起始位置开始没有问题(因为即使你的代码也适用于此)。问题从下一次旋转开始(特别是在更改旋转轴时),因为局部坐标系已经改变。全局视图轮换也是如此,因为它会搞砸所有这些。 - 如何补救改变局部坐标系?
我提出了一个模糊的解决方案,我首先匹配每个坐标系的轴。为了检测哪个轴我只是查询方向的点积与变换矩阵的所有轴,并选择具有最高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度,否则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:
[Edit1]我在我的RubiCube中添加了简单的求解器
为了实现一个求解器,我在我的RubiCube内部表示中添加了表面平面颜色图(在左边...中间正方形是我使用的边的名称和索引)。我还为求解器添加内部命令que(右侧的轴和方向):
每个命令由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(就像人类解决真正的立方体一样):
- 输入状态(不是一步)
- 黄色中间的白色十字架(黄色中间面向前方)
- 白色十字架(白色中间朝向前方)
- 白色角落(白色面朝下)
- 中间层(使用前3个命令)
- 顶层黄色交叉(使用第4个命令)
- 重新排序交叉所以双方匹配(第5命令)和重新排序角落(第6命令)
- 定位顶层角落以完成立方体(第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
这通常应该解决问题..(请告诉我,如果我写错了,因为我写的很难)
至于旋转对象本身的方法,你应该找出对象自己的坐标系中的四元数是什么,并围绕那个坐标系旋转。如果你有一些数学技能,这也很
以上是关于四元数旋转不起作用的主要内容,如果未能解决你的问题,请参考以下文章