我正在遵循google的OpenGL es旋转示例来在我的Android应用上旋转一个简单的正方形(不是立方体),例如以下代码:
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
如果仅绕一个轴旋转,则效果很好。
但是,如果绕一个轴旋转,然后再绕另一轴旋转,则旋转是不公平的。我的意思是,旋转是围绕基本(全局)坐标系的轴进行的,而不是围绕正方形自己的坐标系进行的。
使用Shahbaz的代码进行编辑
public void onDrawFrame(GL10 gl) {
//Limpiamos pantalla y Depth Buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
//Dibujado
gl.glTranslatef(0.0f, 0.0f, z); //Move z units into the screen
gl.glScalef(0.8f, 0.8f, 0.8f); //Escalamos para que quepa en la pantalla
//Rotamos sobre los ejes.
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
//Dibujamos el cuadrado
square.draw(gl);
//Factores de rotación.
xrot += xspeed;
yrot += yspeed;
}
正方形的绘制:
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW);
//gl.glEnable(GL10.GL_BLEND);
//Bind our only previously generated texture in this case
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
//Point to our vertex buffer
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
//Enable vertex buffer
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//Draw the vertices as triangle strip
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//gl.glDisable(GL10.GL_BLEND);
}
顶点缓冲值:
private FloatBuffer vertexBuffer;
private float vertices[] =
{
-1.0f, -1.0f, 0.0f, //Bottom Left
1.0f, -1.0f, 0.0f, //Bottom Right
-1.0f, 1.0f, 0.0f, //Top Left
1.0f, 1.0f, 0.0f //Top Right
};
.
.
.
public Square(int resourceId) {
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
.
.
.
最佳答案
您应该知道的第一件事是,在OpenGL中,变换矩阵从右开始相乘。这是什么意思?这意味着您编写的最后一个转换将首先应用于该对象。
因此,让我们看一下您的代码:
gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
gl.glTranslatef(0.0f, 0.0f, z);
square.draw(gl);
这意味着,首先,将对象移动到
(0.0f, 0.0f, z)
。然后围绕Z旋转,然后围绕Y旋转,然后围绕X旋转,然后按(0.0f, 0.0f, -z)
移动并最终缩放。您的缩放比例正确。您把它放在第一位,所以最后被应用。你也有
gl.glTranslatef(0.0f, 0.0f, -z);
在正确的位置,因为您首先要旋转对象然后再移动它。请注意,旋转对象时,该对象始终绕基本坐标旋转,即(0,0,0)。如果要围绕对象自身的轴旋转对象,则对象本身应位于(0,0,0)中。
所以,在写之前
square.draw(gl);
你应该轮换。现在代码的方式是,将对象移到更远的位置(通过编写
gl.glTranslatef(0.0f, 0.0f, z);
square.draw(gl);
之前),然后旋转,将事情弄糟。删除该行可以使您更接近所需的内容。因此,您的代码将如下所示:gl.glScalef(0.8f, 0.8f, 0.8f);
gl.glTranslatef(0.0f, 0.0f, -z);
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
square.draw(gl);
现在,正方形应旋转到位。
注意:运行此命令后,您将看到正方形的旋转会比较尴尬。例如,如果围绕z旋转90度,则由于先前的旋转,围绕x的旋转看起来就像围绕y的旋转。目前,这对您来说可能还可以,但是如果您希望它看起来真的很好,则应该这样做:
想象一下,您不是在旋转对象,而是在对象周围旋转照相机,看着该对象。通过更改
xrot
,yrot
和zrot
,您可以在对象周围的球体上移动相机。然后,找出相机的位置后,您可以进行数学运算并获取正确的参数来调用glRotatef
和glTranslatef
,或者使用gluLookAt
。这需要对数学和3d想象力有所了解。因此,如果您第一天做得不好,请不要沮丧。
编辑:这是如何沿旋转的对象坐标旋转的想法;
首先,假设您围绕z进行旋转。所以你有
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z
现在,全局Y单位向量显然为(0,1,0),但是对象已旋转,因此其Y单位向量也已旋转。该向量由下式给出:
[cos(zrot) -sin(zrot) 0] [0] [-sin(zrot)]
[sin(zrot) cos(zrot) 0] x [1] = [ cos(zrot)]
[0 0 1] [0] [ 0 ]
因此,围绕y的旋转应如下所示:
gl.glRotatef(yrot, -sin(zrot), cos(zrot), 0.0f); //Y-object
到目前为止,您可以尝试此操作(禁用围绕x的旋转),然后看看它看起来像您想要的样子(我做到了,它起作用了)。
现在对于x,它变得非常复杂。为什么?因为,X单位矢量不仅首先绕z矢量旋转,而且绕着
(-sin(zrot), cos(zrot), 0)
矢量旋转。所以现在对象坐标系中的X单位向量是
[cos(zrot) -sin(zrot) 0] [1] [cos(zrot)]
Rot_around_new_y * [sin(zrot) cos(zrot) 0] x [0] = Rot_around_new_y * [sin(zrot)]
[0 0 1] [0] [0 ]
我们称这个向量(u_x,u_y,u_z)。然后,您的最终旋转(围绕X的旋转)将像这样:
gl.glRotatef(xrot, u_x, u_y, u_z); //X-object
所以!如何找到矩阵
Rot_around_new_y
?有关围绕任意轴旋转的信息,请参见here。转到第6.2节的第一个矩阵,获取3 * 3子矩阵旋转(忽略与平移相关的最右边的列),并将(-sin(zrot), cos(zrot), 0)
用作(u, v, w)
轴,将theta
用作yrot
。我不会在这里做数学运算,因为这需要大量的精力,最终我还是会在那附近的某个地方犯一个错误。但是,如果您非常小心并准备好仔细检查几次,则可以将其写下来并进行矩阵乘法。
附加说明:一种计算
Rot_around_new_y
的方法也可以使用Quaternions。四元数定义为4d向量[xs, ys, zs, c]
,它对应于围绕[x, y, z]
旋转一个角度,该角度为sin
为s
并且其cos
为c
。此
[x, y, z]
是我们的“新Y”,即[-sin(zrot), cos(zrot), 0]
。角度为yrot
。因此,绕Y旋转的四元数为:q_Y = [-sin(zrot)*sin(yrot), cos(zrot)*sin(yrot), 0, cos(yrot)]
最后,如果您有四元数
[a, b, c, d]
,则对应的旋转矩阵is given as:[1 - 2b^2 - 2c^2 2ab + 2cd 2ac - 2bd ]
[ 2ab - 2cd 1 - 2a^2 - 2c^2 2bc - 2ad ]
[ 2ac - 2bd 2bc + 2ad 1 - 2a^2 - 2b^2]