原理:《逐梦旅程:windows游戏编程之从零开始》第21章
utils.h
#pragma once
#include "iostream"
#include "math.h"
using namespace std;
class Vector3;
Vector3 operator - (const Vector3 &v1, const Vector3 &v2);
Vector3 operator * (const Vector3 &v, const float f);
Vector3 operator + (const Vector3 &v1, const Vector3 &v2);
class Vector3
{
public:
float x, y, z;
Vector3()
{
x = y = z = 0;
}
Vector3(float xx, float yy, float zz)
{
x = xx;
y = yy;
z = zz;
}
Vector3(Vector3 &v)
{
x = v.x;
y = v.y;
z = v.z;
}
void set(float dx, float dy, float dz)
{
x = dx;
y = dy;
z = dz;
}
void set(Vector3 &v)
{
x = v.x;
y = v.y;
z = v.z;
}
//翻转该向量
void flip()
{
x = -x;
y = -y;
z = -z;
}
Vector3 cross(Vector3 &v); //返回和b叉乘的结果
float dot(Vector3 &b); //返回和b点积的结果
float len(); //返回向量的长度
void normalize(); //调整该向量到单位长度
friend Vector3 operator - (const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator + (const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator * (const Vector3 &v, const float f);
};
utils.cpp
#include "Utils.h"
//返回和b叉乘的结果
Vector3 Vector3::cross(Vector3 &b)
{
Vector3 v(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x);
return v;
}
//返回和b点乘的结果
float Vector3::dot(Vector3 &b)
{
return x*b.x + y*b.y + z*b.z;
}
//把该向量调整到单位长度
void Vector3::normalize()
{
double len = x*x + y*y + z*z;
if(len < 0.000001)
{
cerr << "\n零向量!";
return;
}
float scale = 1.0 / sqrt(len);
x *= scale;
y *= scale;
z *= scale;
}
//返回向量的长度
float Vector3::len()
{
return sqrt(x*x + y*y + z*z);
}
Vector3 operator - (const Vector3 &v1, const Vector3 &v2)
{
return Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
Vector3 operator * (const Vector3 &v, const float f)
{
return Vector3(v.x*f, v.y*f, v.z*f);
}
Vector3 operator + (const Vector3 &v1, const Vector3 &v2)
{
return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
camera.h
#pragma once
#include <windows.h>
#include <gl/Gl.h>
#include <gl/Glu.h>
#include <gl/glut.h>
#include "utils.h"
class CameraClass
{
private:
//成员变量的声明
Vector3 m_vLeftVector; // 左分量向量
Vector3 m_vLookVector; // 观察方向向量
Vector3 m_vUpVector; // 上分量向量
Vector3 m_vCameraPosition; // 摄像机位置的向量
Vector3 m_vTargetPosition; // 目标观察位置的向量
float m_matView[16];
public:
//四个Set系列函数,注意他们参数都有默认值NULL的,调用时不写参数也可以
//设置摄像机、目标观察位置向量
void Set(Vector3 *pCameraPosition = NULL, Vector3 *pTargetPosition = NULL);
void SetViewMatrix(); //设置取景变换矩阵
void SetShape(float vAngle, float asp, float nr, float fr); //设置视椎体
public:
// 沿各分量平移的三个函数
void MoveAlongLeftVec(float fUnits); // 沿left向量移动
void MoveAlongUpVec(float fUnits); // 沿up向量移动
void MoveAlongLookVec(float fUnits); // 沿look向量移动
// 绕各分量旋转的三个函数
void RotationLeftVec(float fAngle); // 绕left向量旋转
void RotationUpVec(float fAngle); // 绕up向量旋转
void RotationLookVec(float fAngle); // 绕look向量旋转
public:
//构造函数和析构函数
CameraClass(); // 构造函数
virtual ~CameraClass(void); // 析构函数
};
camera.cpp
#include "camera.h"
#define PI 3.14159265
// 构造函数
CameraClass::CameraClass()
{
m_vLeftVector = Vector3(1.0f, 0.0f, 0.0f); // 默认左向量与X正半轴重合
m_vUpVector = Vector3(0.0f, 1.0f, 0.0f); // 默认上向量与Y正半轴重合
m_vLookVector = Vector3(0.0f, 0.0f, 1.0f); // 默认观察向量与Z正半轴重合
m_vCameraPosition = Vector3(0, 0, 4); // 默认摄像机坐标
m_vTargetPosition = Vector3(0, 0, 0); // 默认观察目标位置
memset(m_matView, 0, sizeof(m_matView));
}
// 析构函数
CameraClass::~CameraClass(void)
{
}
//设置相机位置,观察点位置
void CameraClass::Set(Vector3 *pCameraPosition, Vector3 *pTargetPosition)
{
//先看看pCameraPosition是否为默认值NULL
if (pCameraPosition != NULL)
m_vCameraPosition = (*pCameraPosition);
else
m_vCameraPosition = Vector3(0.0f, 0.0f, 4.0f);
//先看看pTargetPosition是否为默认值NULL
if (pTargetPosition != NULL)
m_vTargetPosition = (*pTargetPosition);
else
m_vTargetPosition = Vector3(0.0f, 0.0f, 0.0f);
//观察点位置减摄像机位置,得到观察方向向量
m_vLookVector = m_vCameraPosition - m_vTargetPosition;
SetViewMatrix();
}
//设置取景变换矩阵
void CameraClass::SetViewMatrix()
{
m_vLeftVector = m_vUpVector.cross(m_vLookVector); //左向量与观察向量垂直
m_vUpVector = m_vLookVector.cross(m_vLeftVector); //上向量与左向量、观察向量垂直
m_vLeftVector.normalize(); //规范化左向量
m_vUpVector.normalize(); //规范化上向量
m_vLookVector.normalize(); //规范化m_vLookVector向量
//依次写出取景变换矩阵的第一行
m_matView[0] = m_vLeftVector.x;
m_matView[1] = m_vUpVector.x;
m_matView[2] = m_vLookVector.x;
m_matView[3] = 0.0f;
//依次写出取景变换矩阵的第二行
m_matView[4] = m_vLeftVector.y;
m_matView[5] = m_vUpVector.y;
m_matView[6] = m_vLookVector.y;
m_matView[7] = 0.0f;
//依次写出取景变换矩阵的第三行
m_matView[8] = m_vLeftVector.z;
m_matView[9] = m_vUpVector.z;
m_matView[10] = m_vLookVector.z;
m_matView[11] = 0.0f;
//依次写出取景变换矩阵的第四行
m_matView[12] = -m_vCameraPosition.dot(m_vLeftVector);
m_matView[13] = -m_vCameraPosition.dot(m_vUpVector);
m_matView[14] = -m_vCameraPosition.dot(m_vLookVector);
m_matView[15] = 1.0f;
//取景变换
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(m_matView);
}
//设置视椎体
void CameraClass::SetShape(float vAngle, float asp, float nr, float fr)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//gluPerspective(viewAngle, aspect, nearDis, farDis)
//viewAngle: 视锥体的上下平面的角度,相当于眼睛睁开的程度
//aspect: 观察平面的裁剪窗口的横纵比(w/h),最好和屏幕的一致
//nearDis: 视截体近体距离
//farDis: 视截体远景距离
gluPerspective(vAngle, asp, nr, fr);
glMatrixMode(GL_MODELVIEW);
}
// 沿left向量移动
void CameraClass::MoveAlongLeftVec(float fUnits)
{
//直接乘以fUnits的量来累加就行了
m_vCameraPosition.set(m_vCameraPosition + m_vLeftVector * fUnits);
SetViewMatrix();
}
// 沿up向量移动
void CameraClass::MoveAlongUpVec(float fUnits)
{
//直接乘以fUnits的量来累加就行了
m_vCameraPosition.set(m_vCameraPosition + m_vUpVector * fUnits);
SetViewMatrix();
}
// 沿look向量移动
void CameraClass::MoveAlongLookVec(float fUnits)
{
//直接乘以fUnits的量来累加就行了
m_vCameraPosition.set(m_vCameraPosition + m_vLookVector * fUnits);
SetViewMatrix();
}
// 绕left向量旋转
void CameraClass::RotationLeftVec(float fAngle)
{
fAngle = PI / 180 * fAngle;
float cs = cos(fAngle);
float sn = sin(fAngle);
Vector3 v1(m_vUpVector);
Vector3 v2(m_vLookVector);
m_vUpVector.set(v1 * cs - v2 * sn);
m_vLookVector.set(v1 * sn + v2 * cs);
SetViewMatrix();
}
// 绕up向量旋转
void CameraClass::RotationUpVec(float fAngle)
{
fAngle = PI / 180 * fAngle;
float cs = cos(fAngle);
float sn = sin(fAngle);
Vector3 v1(m_vLookVector);
Vector3 v2(m_vLeftVector);
m_vLookVector.set(v1 * cs - v2 * sn);
m_vLeftVector.set(v1 * sn + v2 * cs);
SetViewMatrix();
}
// 绕look向量旋转
void CameraClass::RotationLookVec(float fAngle)
{
fAngle = PI / 180 * fAngle;
float cs = cos(fAngle);
float sn = sin(fAngle);
Vector3 v1(m_vLeftVector);
Vector3 v2(m_vUpVector);
m_vLeftVector.set(v1 * cs - v2 * sn);
m_vUpVector.set(v1 * sn + v2 * cs);
SetViewMatrix();
}
调用
定义:
CameraClass *camera;
初始化:
camera = new CameraClass();
Vector3 cameraPosition(0, 0, 4);
Vector3 targetPosition(0, 0, 0);
camera->Set(&cameraPosition, &targetPosition);
camera->SetShape(30.0f, WINDOW_WIDTH / WINDOW_HEIGHT, 0.5f, 50.0f);
响应键盘:
void myKeyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 'a': case 'A': camera->MoveAlongLeftVec(-0.01); break;
case 'd': case 'D': camera->MoveAlongLeftVec(0.01); break;
case 'w': case 'W': camera->MoveAlongUpVec(0.01); break;
case 's': case 'S': camera->MoveAlongUpVec(-0.01); break;
case 'q': case 'Q': camera->MoveAlongLookVec(0.01); break;
case 'e': case 'E': camera->MoveAlongLookVec(-0.01); break;
case 'j': case 'J': camera->RotationUpVec(1); break;
case 'l': case 'L': camera->RotationUpVec(-1); break;
case 'i': case 'I': camera->RotationLeftVec(1); break;
case 'k': case 'K': camera->RotationLeftVec(-1); break;
case 'u': case 'U': camera->RotationLookVec(1); break;
case 'o': case 'O': camera->RotationLookVec(-1); break;
default: break;
}
glutPostRedisplay(); //重新绘制
}