在用于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 90度(CCW)产生
和绕Z轴逆时针旋转90度产生第二个
但我希望
我认为问题在于resultVector(我使用的第二个向量,它从原点到视点)无法正确转换。有人知道如何将世界坐标转换为对象坐标吗?有谁知道当物体旋转时我们如何确定物体坐标?
昨天,我决定编写Rubic Cube拼图游戏,因为我过去尝试过的任何事情对我来说都是非常不舒服的,最终我有一些心情/时间自己编写了它。当我完成后,这里是我的见解:
Rubic多维数据集表示形式>>
我不认为四元数是一个很好的选择。相反,我更喜欢:
所以我最终得到了3*3*3=27
变换矩阵的列表以及整个多维数据集旋转的一个附加矩阵。在开始状态下,所有子立方体均具有单位旋转部分,并且其原点设置为覆盖{ -1 , 0 ,+1 }
的所有组合以填充整个Rubic Cube(每个子立方体网格的大小为1.0
,并以(0,0,0)
为中心)] >
我的多维数据集在C ++代码中定义如下:
reper cube[27]; // reper is transform matrix
GUI
我希望控制和查看尽可能地接近真实的事物。因此,可以通过鼠标单击目标子多维数据集(在area0
或area1
中)来控制旋转,然后从鼠标拖动的方向确定旋转的轴和旋转的方向。
从开始位置没有问题(因为即使您的代码也能很好地工作)。问题开始于下一个旋转(尤其是在更改旋转轴时),因为局部坐标系已经更改。全局视图旋转同样如此,因为它将使所有这些混乱。
如何解决改变局部坐标系?
我提出了一个模糊的解决方案,在该解决方案中,我首先从每个坐标系中匹配了轴。要检测哪个轴,我只需对查询方向的点积与变换矩阵的所有轴进行比较,然后选择具有最高绝对点积的轴。该符号仅指示坐标系是否相反(意味着旋转应反向)。
在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)
将矢量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(例如通过人工求解实际的多维数据集):
我做了轻量级的求解器(大约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度时采用这种方法。
我希望这会有所帮助。