Можно ли вращать объект вокруг своей оси, а не вокруг оси базовой координаты? - PullRequest
13 голосов
/ 11 октября 2011

Я следую примеру ротации 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

Работает нормально, если вращаться только вокруг одной оси.

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

РЕДАКТИРОВАТЬ с кодом для Шахбаз

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

Ответы [ 4 ]

24 голосов
/ 11 октября 2011

Первое, что вы должны знать, это то, что в 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.

.

Это требует некоторого понимания математики и трехмерного воображения. Так что, если вы не поняли это правильно в первый день, не расстраивайтесь.

Редактировать: Это идея о том, как вращаться вдоль повернутых координат объекта;

Сначала, скажем, вы делаете вращение вокруг 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 не только сначала вращается вокруг вектора 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) как ось (u, v, w) и theta как yrot.

Я не буду здесь делать математику, потому что это требует больших усилий, и в конце концов я все равно ошибусь где-то там. Однако, если вы очень осторожны и готовы пару раз проверить их дважды, вы можете записать их и выполнить умножения матриц.


Дополнительное примечание: одним из способов вычисления Rot_around_new_y также может быть использование Quaternions . Кватернион определяется как 4-мерный вектор [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]
3 голосов
/ 12 октября 2011

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

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

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

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

3 голосов
/ 11 октября 2011

Я почти ничего не знаю об 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);
0 голосов
/ 11 октября 2011

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

glMatrixMode(GL_MODELVIEW);
glPushMatrix();

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

glPopMatrix();

после рендеринга.Это даст вам дополнительную матрицу вида, с которой можно работать, не затрагивая основную матрицу вида модели.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...