Войти
ПрограммированиеСтатьиОбщее

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

Автор:

В данной статье мы рассмотрим основы работы с OpenGL, а именно работу с матрицами и областями видимости (Viewport).

Начнем с матриц.

В OpenGL существует несколько типов трансформаций, каждый из которых управляется соответствующей матрицей. Для установки текущей матрицы (т.е. той, над которой будут производится операции) используется команда
glMatrixMode(GLenum mode).
Параметр mode может принимать следующие значения:
- GL_MODELVIEW - все последующие изменения будут применяться к объектно-видовой матрице.
- GL_PROJECTION - все последующие изменения будут применяться к проекционной матрице.
- GL_TEXTURE - тоже, но к текстурной матрице.

Объектно-видовая трансформация позиционирует объект относительно камеры, проекционная матрица отвечает за перспективу, т.е. чем дальше объект от "камеры", тем меньше он должен быть нарисован в конечном изображении.

Над матрицами можно производить следующие операции:

glLoadIdentity() - текущая матрица будет сделана единичной.

glTranslatef(GLfloat x, GLfloat y, GLfloat z) - умножает текущую матрицу на матрицу, которая перемещает объект на значения x, y и z или перемещает локальную систему координат на те же значения).

glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) - умножает текущую матрицу на матрицу, которая поворачивает объект (или локальную систему координат) на угол angle против часовой стрелки вокруг луча, проходящего через точку с координатами x, y, z, выходящего из начала координат. Угол поворота задается в градусах.

glScalef(GLfloat x, GLfloat y, GLfloat z) - умножает текущую матрицу на матрицу, которая растягивает, сжимает или отражает объект по осям (фактически просто меняется масштаб :) ). Параметры задают на сколько будут меняться размеры по каждой из осей.

glLoadMatrixf(const GLfloat *m) - заменяет текущую матрицу на матрицу описанную параметром m. m указывает на матрицу 4х4 следующего вида:

Изображение

glMultMatrixf(const GLfloat *m) - умножает текущую матрицу на матрицу, описанную параметром m.

(Для перечисленных функций существует также вариант, когда в качестве параметров можно передавать переменные типа double, тогда в названии функции последняя буква f заменяется на d).

glPushMatrix(), glPopMatrix() - с помощью этих команд мы можем сохранять и восстанавливать матрицы соответственно. Эти функции реализуют стэк матриц, т.е. мы можем сохранять несколько матриц.

Для работы с проекционной матрицей используется функция
glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far).
Данная функция умножает текущую матрицу на проекционную матрицу. Её параметры имеют следующие значения:
left, right - координаты левой и правой вертикальных отсекающих плоскостей.
bottom, top - координаты верхней и нижней горизонтальных отсекающих плоскостей.
near, far - координаты "ближней" и "дальней" плоскостей отсечения.

(Если объект находится за плоскостью отсечения, то он не отображается при редеринге).

Проекционная матрица имеет следующий вид:

Изображение

Значения A, B, C, D рассчитываются следующим образом:

Изображение

Параметры (left, bottom, near) и (right, top, near) определяют точки на "ближней" плоскости отсечения, которые проецируются на нижний левый и правый верхний углы окна соответственно, предполагается, что "глаз" (точка, откуда смотрят) расположена в точке с координатами (0,0,0). Параметр far определяет положение "дальней" плоскости отсечения.

Параметры far и near должны всегда быть положительными.

Следующая функция
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)
позволяет работать с ортографической проекцией (проекционная матрица получается параллельным проецированием), т.е. когда все наши объекты находятся как бы в кубике и вне зависимости от степени удаления от пользователя размер объектов будет оставаться неизменным.

Ортографическая матрица имеет вид:

Изображение

Значения tx, ty, tz рассчитываются следующим образом:

Изображение

Параметры far и near могут быть как положительными так и отрицательными.

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

Viewport - это область окна, в которой будет отображаться результаты нашей работы. Для установки области видимости надо использовать функцию OpenGL
glViewport(GLint x, GLint y, GLsizei width, GLsizei height).
Назначение параметров я думаю ясно, но на всякий случай: x и y - координаты левого верхнего угла области видимости, width и height - размеры. По умолчанию, OpenGL устанавливает область видимости равную размерам окна приложения в момент инициализации. Наиболее часто эту команду используют в обработчике изменения размеров окна.

Эта функция определяет, каким образом будут преобразовываться реальные x и y координаты в оконные координаты. Преобразования происходят следующим образом:

Изображение

где xw и yw - конечные оконные координаты, xnd, ynd - реальные координаты, x, y, width, height - задаются в функции glViewport().

Мы также можем установить, каким образом проецируется z координата из реальных координат в оконные при помощи функции
glDepthRange(GLclampd znear, GLclampd zfar).
Параметр znear проецирует "ближнюю" плоскость отсечения в оконные координаты, zfar - проецирует "дальнюю" плоскость в оконные координаты. После отсечения z координата находится в диапазоне от -1.0 до 1.0 соответствующие "ближней" и "дальней" плоскостям отсечения. Функция glDepthRange() описывает каким образом координаты в этом диапазоне преобразуются в оконные. По умолчанию параметры znear и zfar равны 0 и 1 соответственно, что позволяет полностью использовать весь диапазон значений буфера глубины.

Ну и в конце я хотел бы привести пример по работе с описанными функциями.

Посмотрим, как реализуется процесс изменения области видимости в обработчике изменения размеров окна:

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
  case WM_SIZE:
    switch (wParam)
    {
    case SIZE_MINIMIZED:
      // Тут надо установить флажок, что окно не видно на экране,
      // чтобы не производился рендеринг
      return 0;
    case SIZE_MAXIMIZED:
      // Устанавливаем область видимости
      glViewport (0, 0, (GLsizei)( LOWORD (lParam)), (GLsizei)( HIWORD (lParam)));
      glMatrixMode (GL_PROJECTION); // Устанавливаем текущей матрицу проекции
      glLoadIdentity (); // делаем ее единичной
      gluPerspective (45*(GLfloat)( LOWORD (lParam))/(GLfloat)( HIWORD (lParam)),
            (GLfloat)( HIWORD (lParam))/(GLfloat)( LOWORD (lParam)),
            0.0, 1.0); // считаем проекционную матрицу с помощью утилитной функции
      glMatrixMode (GL_MODELVIEW); // Устанавливаем видовую матрицу
      glLoadIdentity ();// делаем ее единичной

      return 0;
    case SIZE_RESTORED:
      // Устанавливаем область видимости
      glViewport (0, 0, (GLsizei)( LOWORD (lParam)), (GLsizei)( HIWORD (lParam)));
      glMatrixMode (GL_PROJECTION); // Устанавливаем текущей матрицу проекции
      glLoadIdentity (); // делаем ее единичной
      gluPerspective (45*(GLfloat)( LOWORD (lParam))/(GLfloat)( HIWORD (lParam)),
            (GLfloat)( HIWORD (lParam))/(GLfloat)( LOWORD (lParam)),
            0.0, 1.0); // считаем проекционную матрицу с помощью утилитной функции
      glMatrixMode (GL_MODELVIEW); // Устанавливаем видовую матрицу
      glLoadIdentity ();// делаем ее единичной
      
      return 0;
    }
    break;
  }
  return DefWindowProc (hWnd, uMsg, wParam, lParam);
}

#математика, #матрицы, #трансформации

21 февраля 2002

Комментарии [4]