在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);
现在的问题是它只适用于第一次旋转。无论第一次轮换是什么。它运作良好,但对于下一轮旋转,它似乎得到了错误的轴和度数。
例如,如果坐标系是
然后第一次旋转绕逆时针(CCW)X 90度产生
第二次旋转绕Z 90度CCW产生
但我希望
我认为问题是resultVector(我使用的第二个向量来自视点的原点)不能正确转换。谁知道如何将世界坐标转换为对象坐标?谁知道当物体旋转时我们如何确定物体坐标?
好吧昨天我决定编写Rubic Cube拼图,因为我过去尝试过的任何东西对我来说真的很不舒服,终于有了一些心情/时间来自己编写代码。我完成它已经在这里是我的见解:
3*3*3=27
变换矩阵列表以及一个额外的整个立方体旋转列表。在开始状态中,所有子立方体都具有单位旋转部分,并且原点设置为覆盖{ -1 , 0 ,+1 }
的所有组合以填充整个Rubic立方体(每个子立方体网格的大小是1.0
并且以(0,0,0)
为中心)
我的多维数据集在C ++代码中定义如下:
reper cube[27]; // reper is transform matrix
area0
或area1
中)来控制旋转,然后从鼠标拖动的方向决定旋转哪个轴以及在哪个方向上旋转。
从起始位置开始没有问题(因为即使你的代码也适用于此)。问题从下一次旋转开始(特别是在更改旋转轴时),因为局部坐标系已经改变。全局视图轮换也是如此,因为它会搞砸所有这些。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)。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(就像人类解决真正的立方体一样):
求解器很简单,可以在字符串上运行(未经优化),所以它有点慢,但无论如何完整的解决方案在我的设置上只需要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
这通常应该解决问题..(请告诉我,如果我写错了,因为我写的很难)
至于旋转对象本身的方法,你应该找出对象自己的坐标系中的四元数是什么,并围绕那个坐标系旋转。如果你有一些数学技能,这也很容易。除此之外,您还可以定义2个角度(X,Y)并通过输入直接更改它们并使用(1,0,0,X)和(0,1,0,Y)的四元数但是可能存在问题当Y为90度时这种方法..
我希望这有帮助。