Это последний урок по камере. Возможно, позже мы напишем пример движения камеры по кривой безье. Этот урок показывает реализацию стрейфа камеры в стороны.

На первый взгляд всё выглядит несложно, но если вы не знаете линейной алгебры, у вас могут возникнуть сложности.

Стрейф камеры — это движение по вектору, стоящему на 90 градусов от вектора взгляда.
Другими словами, это как если вы делаете шаг в сторону, смотря вперёд.

Поняв, что такое стрейфинг, рассмотрим, как он работает.

Мы знаем, что нам нужно двигаться в направлении 90 градусов от нашего вектора взгляда (который == m_vView — m_vPosition). Итак, как нам получить вектор, перпендикулярный вектору взгляда? Если вы вспомните формулу cross(), вы поймете, как это сделать. cross() — математическая формула, принимающая 2 вектора (по сути плоскость), и возвращающая вектор, стоящий к ним на 90 градусов. Итак, у нас есть вектор взгляда. Где взять второй вектор? Приходит на ум вертикальный вектор? Это он! Нам нужен результат cross-a между вектором взгляда и вертикальным вектором. Результат работы функции будет нужным нам вектором. Во многих играх вертикальный вектор будет меняться, потому как вы будете бегать, прыгать и летать в любых направлениях. Cross() даст нам уверенность, что мы всегда стрейфимся в нужном направлении, не взирая на ориентацию камеры.

Файл init.cpp

Ничего не было изменено с прошлого урока.

Файл main.h

Внимание! Из файла убран класс камеры и помещён в camera.h!

Уберите из файла класс camera.h,
Уберите из файла строку

extern CCamera  g_Camera;

 

Файл camera.h

Внимание! Новый файл!
Этот файл был создан, чтобы вы могли использовать код камеры в собственных проектах, без копипаста кода. Для этого этот файл и camera.cpp Должны быть добавлены в проект.

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

Содержание файла camera.h:

#ifndef _CAMERA_H
#define _CAMERA_Hclass CCamera {
public:
CCamera();/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Это функции для доступа извне к приватным данным
CVector3 Position() {   return m_vPosition; }
CVector3 View()     {   return m_vView; }
CVector3 UpVector() {   return m_vUpVector; }
CVector3 Strafe()   {   return m_vStrafe;}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *

void PositionCamera(float positionX, float positionY, float positionZ,
float viewX,     float viewY,     float viewZ,
float upVectorX, float upVectorY, float upVectorZ);

void RotateView(float angle, float X, float Y, float Z);
void SetViewByMouse();
void RotateAroundPoint(CVector3 vCenter, float X, float Y, float Z);

// Новая функция — стрейф камеры
void StrafeCamera(float speed);

void MoveCamera(float speed);

void CheckForMovement();

/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
// Функция обновляет взгляд камеры и другие данные
void Update();
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *

// Функция использует gluLookAt(), чтобы сказать OpenGL, куда смотреть
void Look();

private:
CVector3 m_vPosition;
CVector3 m_vView;
CVector3 m_vUpVector;
CVector3 m_vStrafe;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
};
#endif

 

Файл camera.cpp

В этот файл мы добавили стрейфинг. Ещё 3 функции добавлены, чтобы ещё немного очистить main.cppp

//В начале файла включите camera.h
#include «main.h»
#include «camera.h»// Добавьте в начало файла строку обьявления скорости камеры,
// а из main.cpp её удалите!
#define kSpeed  0.1f// Удалите из файла функцию RotateAroundPoint().

///////////////////////////////// STRAFE CAMERA \\\\\\\\\\\\\\\\*
/////
/////              стрейфит камеру в стороны:
/////
///////////////////////////////// STRAFE CAMERA \\\\\\\\\\\\\\\\*
void CCamera::StrafeCamera(float speed)
{
// добавим вектор стрейфа к позиции
m_vPosition.x += m_vStrafe.x * speed;
m_vPosition.z += m_vStrafe.z * speed;

// Добавим теперь к взгляду
m_vView.x += m_vStrafe.x * speed;
m_vView.z += m_vStrafe.z * speed;
}

// В функцию MoveCamera(), после строки
// «CVector3 vVector = m_vView — m_vPosition;», добавьте:
vVector.y = 0.0f;
vVector = Normalize(vVector);
// Теперь мы нормализуем наш вектор взгляда, когда двигаемся по миру.
// Это НЕОБХОДИМО делать. Таким образом вы не будете двигатся быстрее, когда
// стрейфитесь, т.к. вектор стрейфа тоже нормализован.

// Добавляем в класс следующие 3 функции:

//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\*
/////
/////           обрабатываем ввод
/////
//////////////////////////// CHECK FOR MOVEMENT \\\\\\\\\\\\\\*

void CCamera::CheckForMovement()
{
// нажаты ли ВВЕРХ или W
if(GetKeyState(VK_UP) & 0x80 || GetKeyState(‘W’) & 0x80) {
MoveCamera(kSpeed);
}

// ВНИЗ или S
if(GetKeyState(VK_DOWN) & 0x80 || GetKeyState(‘S’) & 0x80) {
MoveCamera(kSpeed);
}

// LEFT или A
if(GetKeyState(VK_LEFT) & 0x80 || GetKeyState(‘A’) & 0x80) {

// стрейфим влево
StrafeCamera(kSpeed);
}

// RIGHT или D
if(GetKeyState(VK_RIGHT) & 0x80 || GetKeyState(‘D’) & 0x80) {
// стрейфим вправо
StrafeCamera(kSpeed);
}
}

///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\*
/////
/////           Обновляет вектор взгляда и верт. вектор
/////
///////////////////////////////// UPDATE \\\\\\\\\\\\\\\\*

void CCamera::Update()
{
// Ниже мы рассчитываем вектор стрейфа каждый раз, когда мы апдейтим
// камеру. Это необходимо, т.к. значения используются во многих местах.

// Иниц. переменную для результата cross
CVector3 vCross = Cross(m_vView m_vPosition, m_vUpVector);

//Нормализуем вектор стрейфа
m_vStrafe = Normalize(vCross);

// Посмотрим, двигалась ли мыша
SetViewByMouse();

// Проверим нажатия
CheckForMovement();
}

///////////////////////////////// LOOK \\\\\\\\\\\\\\\\*
/////
/////           Обновляет взгляд камеры
/////
///////////////////////////////// LOOK \\\\\\\\\\\\\\\\*

void CCamera::Look()
{
// Дадим OpenGL позицию, взгляд и верт. вектор камеры
gluLookAt(m_vPosition.x, m_vPosition.y, m_vPosition.z,
m_vView.x,     m_vView.y,     m_vView.z,
m_vUpVector.x, m_vUpVector.y, m_vUpVector.z);
}

 

Файл main.cpp

w, s, UP_ARROW, DOWN_ARROW — двигают камеру вперед-назад.
a, d, RIGHT_ARROW, LEFT_ARROW — стрейфят.
Движение мышью — изменение взгляда.
ESC — выход.

// В начале файла включите «camera.h»
#include «camera.h»// Удалите функцию KeyPressed()// В функции MainLoop() замените функцию KeyPressed(); на:

WPARAM MainLoop()
{
…..
…..
…..
g_Camera.Update();
…..
…..
}

///////////////////////////////// DRAW SPIRAL TOWERS \\\\\\\\\\\\\\\\*
/////
/////   Новая функция!
/////   Рисуем спираль из пирамид в пространстве нашего мира
/////
///////////////////////////////// DRAW SPIRAL TOWERS \\\\\\\\\\\\\\\\*

void DrawSpiralTowers()
{
const float PI = 3.14159f;
const float kIncrease = PI / 16.0f;
const float kMaxRotation = PI * 6;
float radius = 40;

for(float degree = 0; degree < kMaxRotation; degree += kIncrease)
{
float x = float(radius * cos(degree));
float z = float(radius * sin(degree));
CreatePyramid(x, 3, z, 1, 3);
radius -= 40 / (kMaxRotation / kIncrease);
}
}

///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\*
/////
/////   А RenderScene() теперь выглядит так:
/////
///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\*

void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Вместо вызова gluLookAt() мы используем Look классы Camera.
// Кроме подчистки RenderScene(), это ещё и более обьектно-ориентированный
// подход. Если вы пишите движок, вам врядли нужны здесь специфические
// вызовы API.
g_Camera.Look();

// Рисуем 3д сетку
Draw3DSGrid();

// Рисуем пирамиды спиралью в центре мира
DrawSpiralTowers();

SwapBuffers(g_hDC);
}

// В коде WinProc() в блоке WM_KEYDOWN: оставьте только обработку ESC-a:
case WM_KEYDOWN:
switch(wParam) {
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;

 

На этом работа с классом камеры закончена.

Исходные коды к уроку