или в матричной форме

Что получится, если объединить два рассмотренных преобразования? В этом случае получаем композицию двух преобразований, которая в матричной форме записывается в виде:

В общем виде обобщённая 4х4-матрица преобразования для трёхмерных однородных координат имеет вид:

T может быть представлена в виде блочной матрицы:

Подматрица

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

производит перенос, вектор

- перспективное преобразование, а последний скалярный элемент s - общее изменение масштаба." [1]
Матрица преобразования в OpenGL
OpenGL хранит матрицу преобразования в следующем виде:

Заметьте, что элементы в ней следуют в непривычном порядке - не строка за строкой, а столбец за столбцом. Переход к записи, которая используется в линейной алгебре, производится транспонированием.
OpenGL хранит модельную и видовую матрицу в одной - модельно-видовой матрице (GL_MODELVIEW). Чтобы изменить вид (передвинуть камеру), нужно передвинуть всю сцену в противоположную сторону, применив к матрице обратное преобразование. В частности, для этого служит команда gluLookAt(), однако она не всегда удобна.
Матрицу преобразования можно рассматривать и в другом виде:
3 набора элементов (
m0,
m1,
m2), (
m4,
m5,
m6), (
m8,
m9,
m10) представляют собой координаты ортогонального базиса в некой глобальной системе координат:
- (m0, m1, m2): ось +X; вектор, направленный влево, по умолчанию равен (1, 0, 0)
- (m4, m5, m6): ось +Y; вектор, направленный вверх, по умолчанию равен (0, 1, 0)
- (m8, m9, m10): ось +Z; вектор, направленный вперёд, по умолчанию равен (0, 0, 1)
Элементы верхней левой 3x3 подматрицы отвечают за поворот и масштабирование, то есть их изменяют команды glRotate() и glScale(). Подробнее почитать, как именно изменяются элементы матрицы при повороте, можно тут. Элементы m12, m13, m14 модифицируются командой gTranslate(). Элементы m3, m7, m11 являются тёмной материей и модифицировать их не стоит. [2]
Класс камеры
Код класса камеры портирован отсюда с С++ на С#/OpenTK
class Camera
{
private int viewport_width;
private int viewport_height;
private float[] transform = null;
public Camera(float x, float y, float z, int viewport_width, int viewport_height)
{
this.viewport_width = viewport_width;
this.viewport_height = viewport_height;
transform = new float[16];
transform[0] = 1.0f;
transform[5] = 1.0f;
transform[10] = -1.0f;
transform[15] = 1.0f;
transform[12] = x; transform[13] = y; transform[14] = z;
}
}
Первые 2 переменные хранят размеры области вывода и нужны для установки перспективной матрицы. Массив хранит модельно-видовую матрицу.
Конструктор принимает 3 координаты положения камеры в пространстве и размеры области вывода. Внутри конструктора после сохранения ширины и высоты области вывода мы устанавливаем верхнюю левую 3х3 подматрицу в единичную матрицу (кроме 10 элемента, так как ось oZ направлена не от наблюдателя, а в обратную сторону). Правый нижний элемент матрицы отвечает за масштаб и всегда равен 1. Элементы матрицы с 12 по 14 отвечают за координаты камеры и инициализируются входными параметрами.
internal void MoveLoc(float x, float y, float z, float distance)
{
float dx = x * transform[0] + y * transform[4] + z * transform[8];
float dy = x * transform[1] + y * transform[5] + z * transform[9];
float dz = x * transform[2] + y * transform[6] + z * transform[10];
transform[12] += dx * distance;
transform[13] += dy * distance;
transform[14] += dz * distance;
}
Первые три параметра определяют расстояние, на которое будет передвинута камера в локальной системе координат вдоль каждой из осей. Четвёртый параметр является масштабным множителем, для первых трёх. Его назначение в этом методе неясно, но я не стал менять код, а просто всегда передаю 1 в качестве его значения. После перемножения входных координат на локальную матрицу преобразования 3х3 (в верхнем левом углу) результаты записываются в глобальную матрицу преобразования в качестве глобальных координат камеры. Визуальным эффектом от вызова метода MoveLoc() может быть, к примеру, движение камеры вперёд, для чего нужно просто передать в метод отрицательное смещение по оси oZ.
internal void MoveGlob(float x, float y, float z, float distance)
{
transform[12] += x * distance;
transform[13] += y * distance;
transform[14] += z * distance;
}
В методе MoveGlob() передаваемые координаты сразу записываются в глобальную матрицу преобразования и перемещают камеру вдоль глобальных координатных осей. В какую бы сторону ни была повёрнута камера, она всегда будет двигаться вдоль одной прямой в мировом пространстве при вызове с одними и теми же параметрами.
internal void RotateLoc(float deg, float x, float y, float z)
{
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadMatrix(transform);
GL.Rotate(deg, x, y, z);
GL.GetFloat(GetPName.ModelviewMatrix, transform);
GL.PopMatrix();
}
Метод RotateLoc() первым параметром принимает угол, на который нужно повернуть камеру в локальном базисе, а оставшиеся три параметра задают вектор, вокруг которого будет происходить поворот. Вместо ручного расчёта элементов матрицы преобразования используется вызов Rotate(). Он изменяет только на верхнюю левую подматрицу 3х3 матрицы преобразования.
internal void RotateGlob(float deg, float x, float y, float z)
{
float dx = x * transform[0] + y * transform[1] + z * transform[2];
float dy = x * transform[4] + y * transform[5] + z * transform[6];
float dz = x * transform[8] + y * transform[9] + z * transform[10];
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadMatrix(transform);
GL.Rotate(deg, dx, dy, dz);
GL.GetFloat(GetPName.ModelviewMatrix, transform);
GL.PopMatrix();
}
Метод RotateGlob отличается от предыдущего тем, что предварительно вектор, относительно которого происходит поворот, переводится локальные координаты.
internal void SetView()
{
GL.MatrixMode(MatrixMode.Projection);
Matrix4 mat = Matrix4.CreateOrthographicOffCenter(-this.viewport_width / 2,
this.viewport_width / 2,
-this.viewport_height / 2,
this.viewport_height / 2, -1, 1);
GL.LoadMatrix(ref mat);
GL.MatrixMode(MatrixMode.Modelview);
float[] viewmatrix = new float[]{
transform[0], transform[4], -transform[8], 0,
transform[1], transform[5], -transform[9], 0,
transform[2], transform[6], -transform[10], 0,
-(transform[0]*transform[12] +
transform[1]*transform[13] +
transform[2]*transform[14]),
-(transform[4]*transform[12] +
transform[5]*transform[13] +
transform[6]*transform[14]),
(transform[8]*transform[12] +
transform[9]*transform[13] +
transform[10]*transform[14]), 1};
GL.LoadMatrix(viewmatrix);
}
Ключевой метод SetView(), который нужно вызывать каждый кадр перед отрисовкой всех объектов, устанавливает из матрицы transform значения для верхней левой подматрицы 3х3 простым копированием соответствующих элементов (с учётом инвертированной оси oZ), а вектор положения камеры в пространстве предварительно умножается на базисные векторы, чтобы получить соответствующие координаты камеры в новой системе координат.
Список литературы
1. Тихомиров Ю.В. OpenGL. Программирование трёхмерной графики. - 2-е изд. - СПб.: БВЧ-Петербург, 2002. - 304 с.: ил., с. 120-123.
2.
OpenGL Transformation
Страницы:
1 2 Следующая »
#OpenGL, #камера
25 августа 2011
(Обновление: 20 сен 2012)
Комментарии [9]