(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Организация камеры в 3D играх

Источник: delphisources
XProger

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

   Итак, что такое камера в 3D игре? Это виртуальное "око" игрока, то, посредством чего он воспринимает игру визуально. В понятие "камера" входят: угол обзора и положение которое задается радиус вектором и 3 углами относительно осей координат.

 Один из самых простых методов выглядит так:
 

 procedure TCamera.SetRender;
 begin
  gluLookAt(e.X, e.Y, e.Z, c.X, c.Y, c.Z, Up.X, Up.Y, Up.Z);
 end;

   В процедуру gluLookAt передается всего 3 радиус-вектора:
      e - точка в которую обращена камера
      c - положение камеры в пространстве
      Up - указывает направление "вверх" для камеры.
   По-поводу первых двух надеюсь вопросов нет, но вот с вычислением третьего придется изрядно попотеть…

   Однако, зачем что-то вычислять, если это можно доверить графическому API?
 

 procedure TCamera.SetRender;
 begin
  glLoadIdentity;
  glRotatef(Angle.Z, 0, 0, 1);
  glRotatef(Angle.X, 1, 0, 0);
  glRotatef(Angle.Y, 0, 1, 0);
  glTranslatef(-Pos.X, -Pos.Y, -Pos.Z);
 end;

 где
  Angle - вектор описывающий углы поворота относительно каждой из осей координат (в градусах);
  Pos - положение камеры в пространстве, также задающееся радиус-вектором.

   Данный метод бесспорно и прост и удобен, но такие операции как glRotate и glTranslate производят умножение видовой матрицы на другую матрицу. Это конечно же не критично для современных компьютеров, но все же вполне оптимизируемо. Чем мы и займемся…

   Для того чтобы что-либо оптимизировать мы должны понять принцип работы всех трех операций.
   Как известно, перед выводом геометрии на дисплей над ней производится несколько операций, а именно - умножение на матрицу вида и матрицу проекции.
   Нам же достаточно работы с матрицей вида (MODELVIEW). Сама же матрица вида представляется 16 вещественными числами, т.е. матрица имеет размерность 4х4.

   Итак, разберем все операции из предыдущего примера по отдельности:
  glLoadIdentity - преобразует текущую (видовую, проекции, текстуры) матрицу в единичную
 

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


  glRotatef
- домножает текущую матрицу вида на матрицу поворота относительно одной из осей координат.
   Имея 3 оси координат, соответственно можно вычислить всего 3 матрицы поворота:

Относительно оси OX:
 

 1  0  0  0
 0  c  s  0
 0 -s  c  0
 0  0  0  1

Относительно оси OY:
 

 c  0 -s  0
 0  1  0  0
 s  0  c  0
 0  0  0  1

Относительно оси OZ:
 

 c  s  0  0
-s  c  0  0
 0  0  1  0
 0  0  0  1

 где s и c - соответственно синусы и косинусы угла поворота.

  glTranslatef - производит домножение матрицы вида на матрицу сдвига:
 

 1  0  0  0
 0  1  0  0
 0  0  1  0
 x  y  z  1

 где x, y, z - приращение к соответствующим координатам векторов в новой системе координат.

   Итак, с сутью операций разобрались, теперь можно приступить к оптимизации, которая будет заключаться в ручном вычислении матрицы вида!
   Для этого нам понадобятся 3 угла и позиция камеры.
   Нам необходимо перемножить 3 матрицы поворота, и порядок их перемножения которых имеет большое значение.
   В итоге, перемножение матриц [Z]*[X]*[Y] будет выглядеть так:
 

 [ E  F  0 ]   [ 1  0  0 ]   [ C  0 -D ]   [ CE+BDF AF BCF-ED ]
 [-F  E  0 ] X [ 0  A  B ] X [ 0  1  0 ] = [ BDE-CF AE DF+BCE ]
 [ 0  0  1 ]   [ 0 -B  A ]   [ D  0  C ]   [     AD -B     AC ]

 где
 

A = cos(Angle.X);
B = sin(Angle.X);
C = sin(Angle.Y);
D = cos(Angle.Y);
E = cos(Angle.Z);
F = sin(Angle.Z);

   Заметьте, что C и D определены "не верно", т.к. мы попутно приводим матрицу к некоему базису. Это связано с направлением оси Z в OpenGL.

   Теперь необходимо рассчитать матрицу вида, которая выгладит так:
 

 x.x    x.y    x.z   -dot(x, Pos)
 y.x    y.y    y.z   -dot(y, Pos)
 z.x    z.y    z.z   -dot(z, Pos)
   0      0      0             1

 где x, y, z - вектора построенные на соответствующих компонентах матрицы:
 

 x = (CE+BDF, AF, BCF-ED)
 y = (BDE-CF, AE, DF+BCE)
 z = (    AD, -B,     AC)

   Pos - положение камеры в пространстве.
   Операция dot осуществляет скалярное произведение векторов.

   И сам код осуществляющий расчет:
 

 procedure TCamera.SetRender;
 var
  A, B, C, D, E, F : single;
  cx, cy, cz       : TVector;
 begin
  with Angle do
   begin
   A := cos(X);
   B := sin(X);
   C := sin(Y);
   D := cos(Y);
   E := cos(Z);
   F := sin(Z);
   end;
  cx := Vector(C*E+B*D*F, A*F, B*C*F-E*D);
  cy := Vector(B*D*E-C*F, A*E, D*F+B*C*E);
  cz := Vector(A*D,        -B,       A*C);
  // заполнение матрицы
  m[0]:=cx.X;  m[4]:=cx.Y;  m[8] :=cx.Z;  m[12]:=-V_Dot(cx, Pos);
  m[1]:=cy.X;  m[5]:=cy.Y;  m[9] :=cy.Z;  m[13]:=-V_Dot(cy, Pos);
  m[2]:=cz.X;  m[6]:=cz.Y;  m[10]:=cz.Z;  m[14]:=-V_Dot(cz, Pos);
  m[3]:=0;     m[7]:=0;     m[11]:=0;     m[15]:=1;
  // установка матрицы проекции
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(FOV, Width/Height, 0.1, 100);
  glMatrixMode(GL_MODELVIEW);
  // установка матрицы вида
  glLoadMatrixf(@m);
 end;

   Здесь матрица описывается в виде одномерного массива из 16 элементов типа single.
 

 m: array [0..15] of single;

   Углы (Angle) задаются в радианах.
   FOV - угол обзора камеры, который рекомендуется ставить равным 90;
   Width и Height - ширина и высота поля вывода соответственно;
   Vector - функция создания переменной типа TVector по трем значениям (x, y, z);
   V_Dot - скалярное умножение векторов (x1*x2 + y1*y2 + z1*z2).

   Сама процедура выглядит устрашающе по сравнению со вторым методом, но работает значительно шустрее. Вызов ее рекомендуется производить перед началом рендеринга карты и объектов на ней, чтобы изменения матрицы вида отразились на их выводе.

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

   Итак, что такое камера в 3D игре? Это виртуальное "око" игрока, то, посредством чего он воспринимает игру визуально. В понятие "камера" входят: угол обзора и положение которое задается радиус вектором и 3 углами относительно осей координат.

 Один из самых простых методов выглядит так:
 

 procedure TCamera.SetRender;
 begin
  gluLookAt(e.X, e.Y, e.Z, c.X, c.Y, c.Z, Up.X, Up.Y, Up.Z);
 end;

   В процедуру gluLookAt передается всего 3 радиус-вектора:
      e - точка в которую обращена камера
      c - положение камеры в пространстве
      Up - указывает направление "вверх" для камеры.
   По-поводу первых двух надеюсь вопросов нет, но вот с вычислением третьего придется изрядно попотеть…

   Однако, зачем что-то вычислять, если это можно доверить графическому API?
 

 procedure TCamera.SetRender;
 begin
  glLoadIdentity;
  glRotatef(Angle.Z, 0, 0, 1);
  glRotatef(Angle.X, 1, 0, 0);
  glRotatef(Angle.Y, 0, 1, 0);
  glTranslatef(-Pos.X, -Pos.Y, -Pos.Z);
 end;

 где
  Angle - вектор описывающий углы поворота относительно каждой из осей координат (в градусах);
  Pos - положение камеры в пространстве, также задающееся радиус-вектором.

   Данный метод бесспорно и прост и удобен, но такие операции как glRotate и glTranslate производят умножение видовой матрицы на другую матрицу. Это конечно же не критично для современных компьютеров, но все же вполне оптимизируемо. Чем мы и займемся…

   Для того чтобы что-либо оптимизировать мы должны понять принцип работы всех трех операций.
   Как известно, перед выводом геометрии на дисплей над ней производится несколько операций, а именно - умножение на матрицу вида и матрицу проекции.
   Нам же достаточно работы с матрицей вида (MODELVIEW). Сама же матрица вида представляется 16 вещественными числами, т.е. матрица имеет размерность 4х4.

   Итак, разберем все операции из предыдущего примера по отдельности:
  glLoadIdentity - преобразует текущую (видовую, проекции, текстуры) матрицу в единичную
 

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


  glRotatef
- домножает текущую матрицу вида на матрицу поворота относительно одной из осей координат.
   Имея 3 оси координат, соответственно можно вычислить всего 3 матрицы поворота:

Относительно оси OX:
 

 1  0  0  0
 0  c  s  0
 0 -s  c  0
 0  0  0  1

Относительно оси OY:
 

 c  0 -s  0
 0  1  0  0
 s  0  c  0
 0  0  0  1

Относительно оси OZ:
 

 c  s  0  0
-s  c  0  0
 0  0  1  0
 0  0  0  1

 где s и c - соответственно синусы и косинусы угла поворота.

  glTranslatef - производит домножение матрицы вида на матрицу сдвига:
 

 1  0  0  0
 0  1  0  0
 0  0  1  0
 x  y  z  1

 где x, y, z - приращение к соответствующим координатам векторов в новой системе координат.

   Итак, с сутью операций разобрались, теперь можно приступить к оптимизации, которая будет заключаться в ручном вычислении матрицы вида!
   Для этого нам понадобятся 3 угла и позиция камеры.
   Нам необходимо перемножить 3 матрицы поворота, и порядок их перемножения которых имеет большое значение.
   В итоге, перемножение матриц [Z]*[X]*[Y] будет выглядеть так:
 

 [ E  F  0 ]   [ 1  0  0 ]   [ C  0 -D ]   [ CE+BDF AF BCF-ED ]
 [-F  E  0 ] X [ 0  A  B ] X [ 0  1  0 ] = [ BDE-CF AE DF+BCE ]
 [ 0  0  1 ]   [ 0 -B  A ]   [ D  0  C ]   [     AD -B     AC ]

 где
 

A = cos(Angle.X);
B = sin(Angle.X);
C = sin(Angle.Y);
D = cos(Angle.Y);
E = cos(Angle.Z);
F = sin(Angle.Z);

   Заметьте, что C и D определены "не верно", т.к. мы попутно приводим матрицу к некоему базису. Это связано с направлением оси Z в OpenGL.

   Теперь необходимо рассчитать матрицу вида, которая выгладит так:
 

 x.x    x.y    x.z   -dot(x, Pos)
 y.x    y.y    y.z   -dot(y, Pos)
 z.x    z.y    z.z   -dot(z, Pos)
   0      0      0             1

 где x, y, z - вектора построенные на соответствующих компонентах матрицы:
 

 x = (CE+BDF, AF, BCF-ED)
 y = (BDE-CF, AE, DF+BCE)
 z = (    AD, -B,     AC)

   Pos - положение камеры в пространстве.
   Операция dot осуществляет скалярное произведение векторов.

   И сам код осуществляющий расчет:
 

 procedure TCamera.SetRender;
 var
  A, B, C, D, E, F : single;
  cx, cy, cz       : TVector;
 begin
  with Angle do
   begin
   A := cos(X);
   B := sin(X);
   C := sin(Y);
   D := cos(Y);
   E := cos(Z);
   F := sin(Z);
   end;
  cx := Vector(C*E+B*D*F, A*F, B*C*F-E*D);
  cy := Vector(B*D*E-C*F, A*E, D*F+B*C*E);
  cz := Vector(A*D,        -B,       A*C);
  // заполнение матрицы
  m[0]:=cx.X;  m[4]:=cx.Y;  m[8] :=cx.Z;  m[12]:=-V_Dot(cx, Pos);
  m[1]:=cy.X;  m[5]:=cy.Y;  m[9] :=cy.Z;  m[13]:=-V_Dot(cy, Pos);
  m[2]:=cz.X;  m[6]:=cz.Y;  m[10]:=cz.Z;  m[14]:=-V_Dot(cz, Pos);
  m[3]:=0;     m[7]:=0;     m[11]:=0;     m[15]:=1;
  // установка матрицы проекции
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(FOV, Width/Height, 0.1, 100);
  glMatrixMode(GL_MODELVIEW);
  // установка матрицы вида
  glLoadMatrixf(@m);
 end;

   Здесь матрица описывается в виде одномерного массива из 16 элементов типа single.
 

 m: array [0..15] of single;

   Углы (Angle) задаются в радианах.
   FOV - угол обзора камеры, который рекомендуется ставить равным 90;
   Width и Height - ширина и высота поля вывода соответственно;
   Vector - функция создания переменной типа TVector по трем значениям (x, y, z);
   V_Dot - скалярное умножение векторов (x1*x2 + y1*y2 + z1*z2).

   Сама процедура выглядит устрашающе по сравнению со вторым методом, но работает значительно шустрее. Вызов ее рекомендуется производить перед началом рендеринга карты и объектов на ней, чтобы изменения матрицы вида отразились на их выводе.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 22.04.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
CAD Import .NET Professional пользовательская
Delphi Professional Named User
ZBrush 4R6 Win Commercial Single License ESD
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Реестр Windows. Секреты работы на компьютере
СУБД Oracle "с нуля"
Вопросы и ответы по MS SQL Server
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100