是否可以使对象绕其自身轴旋转而不是绕基坐标轴旋转?

发布于 2024-12-08 23:42:41 字数 2462 浏览 0 评论 0原文

我按照谷歌的 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

如果只绕一个轴旋转,它就可以正常工作。

但是,如果您绕一个轴旋转,然后再绕另一个轴旋转,则旋转是不公平的。我的意思是旋转是围绕基础(全局)坐标系的轴而不是正方形自己的坐标系完成的。

的代码进行编辑

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;
}

使用 Shahbaz 绘制正方形

    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);
.
.
.

I am following the OpenGL es rotation examples from google to rotate a simple square (not a cube) on my Android App, for example this code:

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

It works fine if you only rotate around one axis.

But if you rotate around one axis, and after that, you rotate around another axis, the rotation is not fair. I mean that the rotation is done around the axes of base (global) coordinate system and not the square's own coordinate system.

EDIT with code for 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;
}

Draw of the square:

    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);

}

VERTEX BUFFER VALUES:

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);
.
.
.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

も星光 2024-12-15 23:42:41

首先你应该知道的是,在 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?有关绕任意轴旋转的信息,请参阅此处。转到第 6.2 节,第一个矩阵,获取 3*3 子矩阵旋转(即忽略与平移相关的最右列)并输入 (-sin(zrot), cos(zrot), 0)< /code> 为 (u, v, w) 轴,thetayrot

我不会在这里进行数学计算,因为这需要付出很多努力,而且最终我还是会在某个地方犯错误。但是,如果您非常小心并准备好仔细检查几次,您可以将其写下来并进行矩阵乘法。


附加说明:计算 Rot_around_new_y 的一种方法也可以使用 四元数。四元数定义为 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],则相应的旋转矩阵 给出为

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

First thing you should know is that in OpenGL, transformation matrices are multiplied from right. What does it mean? It means that the last transformation you write gets applied to the object first.

So let's look at your code:

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);    

This means that, first, the object is moved to (0.0f, 0.0f, z). Then it is rotated around Z, then around Y, then around X, then moved by (0.0f, 0.0f, -z) and finally scaled.

You got the scaling right. You put it first, so it gets applied last. You also got

gl.glTranslatef(0.0f, 0.0f, -z);

in the right place, because you first want to rotate the object then move it. Note that, when you rotate an object, it ALWAYS rotates around the base coordinate, that is (0, 0, 0). If you want to rotate the object around its own axes, the object itself should be in (0, 0, 0).

So, right before you write

square.draw(gl);

you should have the rotations. The way your code is right now, you move the object far (by writing

gl.glTranslatef(0.0f, 0.0f, z);

before square.draw(gl);) and THEN rotate which messes things up. Removing that line gets you much closer to what you need. So, your code will look like this:

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);    

Now the square should rotate in place.

Note: After you run this, you will see that the rotation of the square would be rather awkward. For example, if you rotate around z by 90 degrees, then rotating around x would look like rotating around y because of the previous rotation. For now, this may be ok for you, but if you want to it to look really good, you should do it like this:

Imagine, you are not rotating the object, but rotating a camera around the object, looking at the object. By changing xrot, yrot and zrot, you are moving the camera on a sphere around the object. Then, once finding out the location of the camera, you could either do the math and get the correct parameters to call glRotatef and glTranslatef or, use gluLookAt.

This requires some understanding of math and 3d imagination. So if you don't get it right the first day, don't get frustrated.

Edit: This is the idea of how to rotate along rotated object coordinates;

First, let's say you do the rotation around z. Therefore you have

gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z

Now, the global Y unit vector is obviously (0, 1, 0), but the object has rotated and thus its Y unit vector has also rotated. This vector is given by:

[cos(zrot) -sin(zrot) 0]   [0]   [-sin(zrot)]
[sin(zrot)  cos(zrot) 0] x [1] = [ cos(zrot)]
[0          0         1]   [0]   [ 0        ]

Therefore, your rotation around y, should be like this:

gl.glRotatef(yrot, -sin(zrot), cos(zrot), 0.0f);   //Y-object

You can try this so far (disable rotation around x) and see that it looks like the way you want it (I did it, and it worked).

Now for x, it gets very complicated. Why? Because, the X unit vector is not only first rotated around the z vector, but after it is rotated around the (-sin(zrot), cos(zrot), 0) vector.

So now the X unit vector in the object's cooridnate is

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

Let's call this vector (u_x, u_y, u_z). Then your final rotation (the one around X), would be like this:

gl.glRotatef(xrot, u_x, u_y, u_z);   //X-object

So! How to find the matrix Rot_around_new_y? See here about rotation around arbitrary axis. Go to section 6.2, the first matrix, get the 3*3 sub matrix rotation (that is ignore the rightmost column which is related to translation) and put (-sin(zrot), cos(zrot), 0) as the (u, v, w) axis and theta as yrot.

I won't do the math here because it requires a lot of effort and eventually I'm going to make a mistake somewhere around there anyway. However, if you are very careful and ready to double check them a couple of times, you could write it down and do the matrix multiplications.


Additional note: one way to calculate Rot_around_new_y could also be using Quaternions. A quaternion is defined as a 4d vector [xs, ys, zs, c], which corresponds to rotation around [x, y, z] by an angle whose sin is s and whose cos is c.

This [x, y, z] is our "new Y", i.e. [-sin(zrot), cos(zrot), 0]. The angle is yrot. The quaternion for rotation around Y is thus given as:

q_Y = [-sin(zrot)*sin(yrot), cos(zrot)*sin(yrot), 0, cos(yrot)]

Finally, if you have a quaternion [a, b, c, d], the corresponding rotation matrix 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]
属性 2024-12-15 23:42:41

我对 openGL 几乎一无所知,但我想翻译为 0,旋转然后翻译回来应该可以......

gl.glTranslatef(-x, -y, -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(x, y, z);

I know next-to-nothing about openGL, but I imagine translating to 0, rotating and then translating back should work...

gl.glTranslatef(-x, -y, -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(x, y, z);
战皆罪 2024-12-15 23:42:41

我认为你需要四元数来做你想做的事。使用绕坐标轴的旋转有时会起作用,但最终会受到“万向节锁定”的影响。当您想要的旋转靠近坐标轴并在所需绕轴旋转接近 180 度时产生不需要的回转时,就会发生这种情况。

四元数是一个数学对象,表示围绕定义为 3D 向量的任意轴的旋转。要在 openGL 中使用它,您可以从四元数生成一个矩阵,并将其乘以模型视图矩阵。这将改变您的世界坐标,以便旋转正方形。

您可以在此处获取更多信息 http://content.gpwiki.org/index.php /OpenGL:Tutorials:Using_Quaternions_to_represent_rotation

我有一个四元数 C++ 类,如果有帮助的话我可以发送给您。

I think you need quaternions to do what you want to do. Using rotations about the coordinate axes works some of the time, but ultimately suffers from "gimbal lock". This happens when the rotation you want passes close by a coordinate axis and creates an unwanted gyration as the rotation required around the axis approaches 180 degrees.

A quaternion is a mathematical object that represents a rotation about an arbitrary axis defined as a 3D vector. To use it in openGL you generate a matrix from the quaternion and multiply it by your modelview matrix. This will transform your world coordinates so that the square is rotated.

You can get more info here http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation

I have a Quaternion C++ class I could send you if it helps.

通知家属抬走 2024-12-15 23:42:41

尝试

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

在正在旋转的单个立方体的渲染代码之前添加,然后

glPopMatrix();

在渲染完成之后添加。它将为您提供额外的视图矩阵,而不会影响您的主模型视图矩阵。

本质上,它的作用是创建一个新的模型视图相机,渲染,然后销毁它。

Try adding

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

before the render code for a single cube that's being rotated, and then

glPopMatrix();

after the rendering is done. It will give you an extra view matrix to work with without affecting your primary modelview matrix.

Essentially what this does is create a new modelview camera, render, then destroy it.

无尽的现实 2024-12-15 23:42:41

我用的是opentk,不过还是一样的。
首先将对象移动其尺寸的一半,然后旋转并向后移动:

model = Matrix4.CreateTranslation(new Vector3(-width/2, -height / 2, -depth / 2)) * 

Matrix4.CreateRotationX(rotationX) * 

Matrix4.CreateRotationY(rotationY) * 

Matrix4.CreateRotationZ(rotationZ) *

Matrix4.CreateTranslation(new Vector3(width / 2, height / 2, depth / 2));

I'm using opentk, nevertheless it's the same.
First move the object half all it's dimensions size, then rotate and move back:

model = Matrix4.CreateTranslation(new Vector3(-width/2, -height / 2, -depth / 2)) * 

Matrix4.CreateRotationX(rotationX) * 

Matrix4.CreateRotationY(rotationY) * 

Matrix4.CreateRotationZ(rotationZ) *

Matrix4.CreateTranslation(new Vector3(width / 2, height / 2, depth / 2));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文