我正在遵循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的旋转。目前,这对您来说可能还可以,但是如果您希望它看起来真的很好,则应该这样做:

想象一下,您不是在旋转对象,而是在对象周围旋转照相机,看着该对象。通过更改xrotyrotzrot,您可以在对象周围的球体上移动相机。然后,找出相机的位置后,您可以进行数学运算并获取正确的参数来调用glRotatefglTranslatef,或者使用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]旋转一个角度,该角度为sins并且其cosc

[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]

10-08 07:26
查看更多