Трансформации в OpenGL ES
Эта статья посвященная трансформациям в OpenGL ES для iPhone, но также будет полезна всем, кто интересуется OpenGL в целом. Ранее этот материал был опубликован на сайте iphoner.org.ua.
OpenGL использует правостороннюю координатную систему. Это значит, что ось x идет слева направо, ось y — снизу вверх, а ось z — из глубины экрана в сторону передней части экрана.
Перемещение (Translate)
Синтаксис:
public abstract void glTranslatef (float x, float y, float z)
Перемещения влияют на все вершины полигона в равной степени в рамках отдельной оси и отражают простые действия «суммирования» и «вычитания».
Пример: начальная точка имеет координаты {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 — угл вращения.
Несколько моментов:
- Значение угла задается в градусах (не в радианах).
- При нескольких вращениях порядок трансформаций играет роль. Для того, чтобы отменить вращение для 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);
И хотите восстановить начальное состояние, вы не можете использовать такой же подход:
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);
Вы должны использовать трансформации в обратном порядке:
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)
Здесь также важно помнить, что порядок трансформаций играет важную роль.
Есть вы сначала перемещаете фигуру, а затем вращаете ее, то поворот будет осуществляться после перемещения:
Если вы вначале вращаете фигуру, а затем перемещаете, то перемещение будет производиться для новой (наклоненной) системы координат:
Масштабирование (Scale)
Синтаксис:
public abstract void glScalef (float x, float y, float z)
Масштабирование — это умножение вектора на скаляр.
На рисунке показано масштабирование в два раза — gl.glScalef(2f, 2f, 2f).
Перемещение и масштабирование (Translate & Scale)
Если вы перемещаете объект перед масштабированием, то вы получите следующую картину (перемещаем на 2 единицы и уменьшаем в 2 раза):
gl.glTranslatef(2, 0, 0); gl.glScalef(0.5f, 0.5f, 0.5f);
Если же вы масштабируете объект до перемещения вы получите отличный от предыдущего результат — после масштабирования вы переместитесь всего лишь на 1 единицу.
gl.glScalef(0.5f, 0.5f, 0.5f); gl.glTranslatef(2, 0, 0);
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. ...
Ссылки: