Организация камеры в 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 радиус-вектора: Однако, зачем что-то вычислять, если это можно доверить графическому 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; где Данный метод бесспорно и прост и удобен, но такие операции как glRotate и glTranslate производят умножение видовой матрицы на другую матрицу. Это конечно же не критично для современных компьютеров, но все же вполне оптимизируемо. Чем мы и займемся… Для того чтобы что-либо оптимизировать мы должны понять принцип работы всех трех операций. Итак, разберем все операции из предыдущего примера по отдельности: 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Относительно оси 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 - приращение к соответствующим координатам векторов в новой системе координат. Итак, с сутью операций разобрались, теперь можно приступить к оптимизации, которая будет заключаться в ручном вычислении матрицы вида! [ 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 - положение камеры в пространстве. И сам код осуществляющий расчет: 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) задаются в радианах. Сама процедура выглядит устрашающе по сравнению со вторым методом, но работает значительно шустрее. Вызов ее рекомендуется производить перед началом рендеринга карты и объектов на ней, чтобы изменения матрицы вида отразились на их выводе. В данной статье я постараюсь показать принципы организации камеры в играх. Итак, что такое камера в 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 радиус-вектора: Однако, зачем что-то вычислять, если это можно доверить графическому 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; где Данный метод бесспорно и прост и удобен, но такие операции как glRotate и glTranslate производят умножение видовой матрицы на другую матрицу. Это конечно же не критично для современных компьютеров, но все же вполне оптимизируемо. Чем мы и займемся… Для того чтобы что-либо оптимизировать мы должны понять принцип работы всех трех операций. Итак, разберем все операции из предыдущего примера по отдельности: 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Относительно оси 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 - приращение к соответствующим координатам векторов в новой системе координат. Итак, с сутью операций разобрались, теперь можно приступить к оптимизации, которая будет заключаться в ручном вычислении матрицы вида! [ 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 - положение камеры в пространстве. И сам код осуществляющий расчет: 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) задаются в радианах. Сама процедура выглядит устрашающе по сравнению со вторым методом, но работает значительно шустрее. Вызов ее рекомендуется производить перед началом рендеринга карты и объектов на ней, чтобы изменения матрицы вида отразились на их выводе. |