Трансформации в OpenGL ES

четверг, 3 марта 2011, Александр Краковецкий

Эта статья посвященная трансформациям в OpenGL ES для iPhone, но также будет полезна всем, кто интересуется OpenGL в целом. Ранее этот материал был опубликован на сайте iphoner.org.ua.

OpenGL использует правостороннюю координатную систему. Это значит, что ось x идет слева направо, ось y — снизу вверх, а ось z — из глубины экрана в сторону передней части экрана.

alt text

Перемещение (Translate)

Синтаксис:

public abstract void glTranslatef (float x, float y, float z)

Перемещения влияют на все вершины полигона в равной степени в рамках отдельной оси и отражают простые действия «суммирования» и «вычитания».

alt text

Пример: начальная точка имеет координаты {x:-2, y:1}. Мы хотим переместить ее в точку {x:1, y:3}, поэтому добавляем {x:3, y:2}.

Расчеты:

{x:-2, y:1} + {x:3, y:2} = {x:-2 + 3, y:1 + 2} = {x:1, y:3}.

В трехмерном пространстве находясь в точке {x:1, y:1, z:0}, мы добавляем {x:0, y:0, z:-3} и в результате будем находиться в точке {x:1, y:1, z:-3}.

Для того, чтобы увидеть площадь на экране, необходимо добавить {x:0, y:0, z:-4} к текущей позиции. Код:

// Translates 4 units into the screen.
gl.glTranslatef(0, 0, -4); 

Вращение (Rotate)

Синтаксис:

public abstract void glRotatef(float angle, float x, float y, float z)

Вращение матрицы, где x, y и z — координаты вектора вращения, angle — угл вращения.

alt text

Несколько моментов:

  • Значение угла задается в градусах (не в радианах).
  • При нескольких вращениях порядок трансформаций играет роль. Для того, чтобы отменить вращение для glRotatef(angle, x, y, z), необходимо использовать glRotatef(angle, -x, -y, -z) или glRotatef(-angle, x, y, z).

Но если вы хотите применить несколько вращений:

gl.glRotatef(90f, 1.0f, 0.0f, 0.0f); 
gl.glRotatef(90f, 0.0f, 1.0f, 0.0f); 
gl.glRotatef(90f, 0.0f, 0.0f, 1.0f); 

alt text

И хотите восстановить начальное состояние, вы не можете использовать такой же подход:

gl.glRotatef(90f, -1.0f, 0.0f, 0.0f); 
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f); 
gl.glRotatef(90f, 0.0f, 0.0f, -1.0f); 

alt text

Вы должны использовать трансформации в обратном порядке:

gl.glRotatef(90f, 0.0f, 0.0f, -1.0f); 
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f); 
gl.glRotatef(90f, -1.0f, 0.0f, 0.0f); 

Перемещение и вращение (Translate & Rotate)

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

Есть вы сначала перемещаете фигуру, а затем вращаете ее, то поворот будет осуществляться после перемещения:

alt text

Если вы вначале вращаете фигуру, а затем перемещаете, то перемещение будет производиться для новой (наклоненной) системы координат:

alt text

Масштабирование (Scale)

Синтаксис:

public abstract void glScalef (float x, float y, float z) 

Масштабирование — это умножение вектора на скаляр.

На рисунке показано масштабирование в два раза — gl.glScalef(2f, 2f, 2f).

alt text

Перемещение и масштабирование (Translate & Scale)

Если вы перемещаете объект перед масштабированием, то вы получите следующую картину (перемещаем на 2 единицы и уменьшаем в 2 раза):

gl.glTranslatef(2, 0, 0); 
gl.glScalef(0.5f, 0.5f, 0.5f); 

alt text

Если же вы масштабируете объект до перемещения вы получите отличный от предыдущего результат — после масштабирования вы переместитесь всего лишь на 1 единицу.

gl.glScalef(0.5f, 0.5f, 0.5f); 
gl.glTranslatef(2, 0, 0); 

alt text

Load Identity, push и pop matrix

Когда вы используете трансформации, вы не используете одинаковые условия, а применяете их к предыдущему состоянию объекта. Вам необходимо сбрасывать (reset) позиции.

glLoadIdentity:

public abstract void glLoadIdentity() 

glLoadIdentity заменяет текущую матрицу на единичную:

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

glPushMatrix:

public abstract void glPushMatrix() 

glPushMatrix делает копию текущей матрицы и отправляет ее в стэк. Это означает, что все последующие изменения вы будете делать для копии.

glPopMatrix:

public abstract void glPopMatrix() 

Для возвращения к предыдущей матрице используют команду glPushMatrix.

Хорошая практика — вызывать glLoadIdentity в начале каждого фрейма и после этого использовать glPushMatrix и glPopMatrix.

Используем все вместе

Итак, имеем три плоскости — A, B и C. Масштабируем их так, чтобы B была на 50% меньше чем A и C — меньше на 50%, чем B. Потом A будем вращать посредине экрана, B должна вращаться вокруг A и, наконец, C заставим вращаеться вокруг B и одновременно вокруг своего центра с бОльшей скоростью.

Код будет выглядеть следующим образом:

public void onDrawFrame(GL10 gl) {
        // Clears the screen and depth buffer.
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // Replace the current matrix with the identity matrix
        gl.glLoadIdentity();
        // Translates 10 units into the screen.
        gl.glTranslatef(0, 0, -10); 

        // SQUARE A
        // Save the current matrix.
        gl.glPushMatrix();
        // Rotate square A counter-clockwise.
        gl.glRotatef(angle, 0, 0, 1);
        // Draw square A.
        square.draw(gl);
        // Restore the last matrix.
        gl.glPopMatrix();

        // SQUARE B
        // Save the current matrix
        gl.glPushMatrix();
        // Rotate square B before moving it, making it rotate around A.
        gl.glRotatef(-angle, 0, 0, 1);
        // Move square B.
        gl.glTranslatef(2, 0, 0);
        // Scale it to 50% of square A
        gl.glScalef(.5f, .5f, .5f);
        // Draw square B.
        square.draw(gl);                        

        // SQUARE C
        // Save the current matrix
        gl.glPushMatrix();
        // Make the rotation around B
        gl.glRotatef(-angle, 0, 0, 1);
        gl.glTranslatef(2, 0, 0);
        // Scale it to 50% of square B
        gl.glScalef(.5f, .5f, .5f);
        // Rotate around it's own center.
        gl.glRotatef(angle*10, 0, 0, 1);
        // Draw square C.
        square.draw(gl);

        // Restore to the matrix as it was before C.
        gl.glPopMatrix();
        // Restore to the matrix as it was before B.
        gl.glPopMatrix();

        // Increse the angle.
        angle++;
}

    public class OpenGLRenderer implements Renderer {
        private Square square;
        private float angle; // Don't forget to add this.
        ...

Ссылки:


Ищите нас в интернетах!

Комментарии

Свежие вакансии