Этот урок демонстрирует способ программирования вида от 3 лица. На самом деле есть еще немало вещей, связанных с ним, но тут показаны основы.

Сделано всё действительно просто. Вместо вращения вектора взгляда вокруг позиции мы просто вращаем нашу позицию вокруг точки, куда смотрим. Я просто взял RotateView() и поменял в ней местами m_vView и m_vPosition.

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

Файл init.cpp

С прошлого урока ничего не изменилось.

Файл main.h

В описание класса камеры мы добавили функцию RotateAroundPoin().
Это позволило нам вращать камеру вокруг некой точки (В случае игры — вокруг игрового персонажа).

class CCamera {
public:
……….
……….
……….
// Функция вращает камеру вокруг точки (например вашего персонажа).
// vCenter — Точка, вокруг которой она вращается.
void RotateAroundPoint(CVector3 vCenter, float angle, float x, float y, float z);
……….
……….
};

 

В класс камеры мы добавили функцию RotateAroundPoint(). Она принимает точку, вокруг которой требуется вращаться, ось вращения и требуемый угол.

Файл camera.cpp:

///////////////////////////////// ROTATE AROUND POINT \\\\*
/////
/////   This rotates the position around a given point
/////
///////////////////////////////// ROTATE AROUND POINT \\\\*void CCamera::RotateAroundPoint(CVector3 vCenter, float angle, float x, float y, float z)
{
CVector3 vNewPosition;// Чтобы вращать нашу позицию вокруг точки, всё, что нам надо — найти вектор
// от нашей позиции к центральной точке вращения. Один раз получив вектор,
// мы вращаемся вокруг точки с заданной скоростью/углом.
// Новый вектор vCenter — точка, вокруг которой мы вращаемся.

// Получим центр, вокруг которого нужно вращатся
CVector3 vPos = m_vPosition vCenter;

// Вычислим синус и косинус угла
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);

// Найдем значение X точки вращения
vNewPosition.x  = (cosTheta + (1 cosTheta) * x * x)       * vPos.x;
vNewPosition.x += ((1 cosTheta) * x * y z * sinTheta)   * vPos.y;
vNewPosition.x += ((1 cosTheta) * x * z + y * sinTheta)   * vPos.z;

// И значение Y
vNewPosition.y  = ((1 cosTheta) * x * y + z * sinTheta)   * vPos.x;
vNewPosition.y += (cosTheta + (1 cosTheta) * y * y)       * vPos.y;
vNewPosition.y += ((1 cosTheta) * y * z x * sinTheta)   * vPos.z;

// И Z…
vNewPosition.z  = ((1 cosTheta) * x * z y * sinTheta)   * vPos.x;
vNewPosition.z += ((1 cosTheta) * y * z + x * sinTheta)   * vPos.y;
vNewPosition.z += (cosTheta + (1 cosTheta) * z * z)       * vPos.z;

// Теперь просто прибавим новый вектор к нашей позиции,
// чтобы установить новую позицию камеры.
m_vPosition = vCenter + vNewPosition;
}

 

Файл main.cpp
Тут мы только изменяем реакцию на input, а также отрисовку сцены.

///////////////////////////////////////////////////////////////////////////
/////
/////   Добавьте эту функцию в файл main.cpp
/////   Это обработка клавиш, заменяем ею CheckForMovement()
/////
///////////////////////////////////////////////////////////////////////////
void KeyPressed()
{
if(GetKeyState(VK_UP) & 0x80) {             // если нажата «ВВЕРХ»
g_Camera.MoveCamera(kSpeed);            // Движемся вперед
}
if(GetKeyState(VK_DOWN) & 0x80) {           // Если нажата «ВНИЗ»
g_Camera.MoveCamera(kSpeed);           // Назад
}// Мы сделали два изменения с прошлого урока. Вместо gCamera.RotateView()
// мы используем новую ф-ю RotateAroundPoint(), передавая ей точку
// взгляда камеры.
if(GetKeyState(VK_LEFT) & 0x80) {
// Вращаем вокруг оси Y с положительной скоростью
g_Camera.RotateAroundPoint(g_Camera.m_vView, kSpeed, 0, 1, 0);
}
if(GetKeyState(VK_RIGHT) & 0x80) {
// А тут — с отрицательной
g_Camera.RotateAroundPoint(g_Camera.m_vView, kSpeed, 0, 1, 0);
}
}///////////////////////////////// CREATE PYRAMID \\\\*
/////
/////   Новая функция.
/////   Создает пирамиду с центром в X,Y,Z
/////   Ширина и высота передаются в ф-ю.
/////
///////////////////////////////// CREATE PYRAMID \\\\*

void CreatePyramid(float x, float y, float z, int width, int height)
{
// Ниже мы создаем пирамиду. Её составляют 4 треугольника по бокам и квадрат внизу.

// 4 треугольника для сторон:
glBegin(GL_TRIANGLES);
// Задняя сторона
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x width, y height, z width);
glColor3ub(255, 0, 255); glVertex3f(x + width, y height, z width);

// Передняя сторона
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x + width, y height, z + width);
glColor3ub(255, 0, 255); glVertex3f(x width, y height, z + width);

// Левая сторона
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x width, y height, z + width);
glColor3ub(0, 255, 255); glVertex3f(x width, y height, z width);

// Правая сторона
glColor3ub(255, 0, 0);   glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x + width, y height, z width);
glColor3ub(0, 255, 255); glVertex3f(x + width, y height, z + width);
glEnd();

// Теперь дно пирамиды
glBegin(GL_QUADS);
glColor3ub(0, 0, 255); glVertex3f(x width, y height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y height, z width);
glColor3ub(0, 0, 255); glVertex3f(x width, y height, z width);
glEnd();
}

///////////////////////////////// DRAW 3D GRID \\\\*
/////
/////   Ещё одна новая функция.
/////   Она создаёт простую сетку для лучшего восприятия анимации
/////
///////////////////////////////// DRAW 3D GRID \\\\*

void Draw3DSGrid()
{
// Просто рисуем по 100 зеленых линий вертикально и горизонтально по осям X и Z

glColor3ub(0, 255, 0);

// Рисуем сетку 1х1 вдоль осей
for(float i = 50; i <= 50; i+=1)
{
glBegin(GL_LINES);
// Ось Х
glVertex3f(50, 0, i);
glVertex3f(50, 0, i);

// Ось Z
glVertex3f(i, 0, 50);
glVertex3f(i, 0, 50);
glEnd();
}
}

///////////////////////////////// RENDER SCENE \\\\*
/////
/////   Отрисовка сцены теперь выглядит так:
/////
///////////////////////////////// RENDER SCENE \\\\*

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

//Скормим OpenGL данные камеры
gluLookAt(g_Camera.m_vPosition.x, g_Camera.m_vPosition.y, g_Camera.m_vPosition.z,
g_Camera.m_vView.x,     g_Camera.m_vView.y,     g_Camera.m_vView.z,
g_Camera.m_vUpVector.x, g_Camera.m_vUpVector.y,g_Camera.m_vUpVector.z);

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

// Создаем 4 пирамиды, просто для ориентирования в мире
CreatePyramid(6, 3, 6, 1, 6);
CreatePyramid(6, 3, 6, 1, 6);
CreatePyramid(6, 3, 6, 1, 6);
CreatePyramid(6, 3, 6, 1, 6);

// Переместим «персонажа» в точку нашего взгляда. Таким образом
// мы всегда будем смотреть на обьект, а обьект всегда будет перед нами,
// куда бы мы не передвигались.
glTranslatef(g_Camera.m_vView.x, 0, g_Camera.m_vView.z);

// Теперь создаем 2 пирамиды, одна из которых перевернута.
CreatePyramid(0, 2, 0, 1, 1);
glRotatef(180, 1, 0, 0);
CreatePyramid(0, 1, 0, 1, 1);

//*************************************************************************
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho( 0,SCREEN_WIDTH,SCREEN_HEIGHT, 0,0, 1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//*************************************************************************

CalculateFrameRate();
glDisable(GL_DEPTH_TEST);
glColor3f(1.0f,1.0f,1.0f);
wchar_t buf[256];
swprintf(buf, sizeof(buf), L«Текущие FPS: %f», FPS);

Font->Print(10,20, buf);  // Выводим текст
glEnable(GL_DEPTH_TEST);

//*************************************************************************
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
//*************************************************************************

SwapBuffers(g_hDC);
}

// Ну и последнее, в функции MainLoop() замените вызов CheckForMovement() на KeyPressed().

 

Вот и всё. Теперь, скомпилировав программу, вы сможете клавишами влево и вправо вращаться вокруг «персонажа», а клавишами вперед и назад — перемещать его =)

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