На самом деле есть еще немало вещей, связанных с ним, но тут показаны основы.
Сделано всё действительно просто. Вместо вращения вектора взгляда вокруг позиции
мы просто вращаем нашу позицию вокруг точки, куда смотрим. Я просто взял
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);
..........
..........
};
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;
}
/////
///// 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().
/////
///// Добавьте эту функцию в файл 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().
Вот и всё. Теперь, скомпилировав программу, вы сможете клавишами влево и вправо вращаться
вокруг "персонажа", а клавишами вперед и назад - перемещать его =)
Комментарии:
Войдите, чтобы оставить комментарий: