Можно ли вращать объект вокруг своей оси, а не вокруг базовой оси координат?

Я следую примерам ротации OpenGL es из google, чтобы повернуть простой квадрат (а не куб) в моем приложении 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

Он отлично работает, если вы только вращаетесь вокруг одной оси.

Но если вы вращаетесь вокруг одной оси, и после этого вы вращаетесь вокруг другой оси, вращение нечестно. Я имею в виду, что вращение выполняется вокруг осей базовой (глобальной) системы координат, а не квадратной собственной системы координат.

EDIT с кодом для 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);

}

ОЦЕНКИ БУФЕРА VERTEX:

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

Ответ 1

Первое, что вы должны знать, это то, что в 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);) и THEN вращаются, что испортит вещи. Удаление этой линии приближает вас к тому, что вам нужно. Итак, ваш код будет выглядеть так:

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 в объекте cooridnate равен

                   [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) в качестве оси (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], соответствующая матрица вращения задается как:

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

Ответ 2

Я думаю, вам нужны кватернионы, чтобы делать то, что вы хотите сделать. Использование поворотов вокруг осей координат работает некоторое время, но в конечном итоге страдает от "карданного замка". Это происходит, когда вращение, которое вы хотите, проходит близко по координатной оси и создает нежелательное вращение, когда вращение вокруг оси приближается к 180 градусам.

Кватернион - это математический объект, представляющий поворот вокруг произвольной оси, определяемой как трехмерный вектор. Чтобы использовать его в openGL, вы генерируете матрицу из кватерниона и умножаете ее на вашу модельную матрицу. Это преобразует ваши мировые координаты так, что квадрат будет повернут.

Вы можете получить дополнительную информацию здесь http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation

У меня есть класс Quaternion С++, который я могу отправить вам, если это поможет.

Ответ 3

Я знаю почти ничего о 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);

Ответ 4

Попробуйте добавить

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

перед кодом рендеринга для вращения одного куба, а затем

glPopMatrix();

после выполнения рендеринга. Он даст вам дополнительную матрицу представлений для работы без влияния на вашу основную матрицу просмотра модели.

По сути, это то, что создает новую камеру просмотра модели, визуализирует, а затем уничтожает ее.