首页 > 游戏 > 腾讯资深程序员解密游戏引擎开发,建议一定学好数学

腾讯资深程序员解密游戏引擎开发,建议一定学好数学


对很多爱玩游戏的男生来说,游戏行业一定会有值得热爱的工作。但入行之后才知道玩游戏和开发游戏是两回事!


给无数人带去快乐的游戏,开发难度大吗?入行门槛高吗?

无数对游戏行业憧憬的人都有这样的疑问。


我们先从和游戏开发密切相关的引擎说起吧。


游戏引擎技术在国外发展十分迅速,但国内游戏行业起步相对较晚,再加上从业者有时急于求成,引擎的人才积累远远不如国外。


游戏引擎到底是什么呢?


在游戏玩家看来,游戏画面的表现力越好,游戏场面的震撼程度越大,游戏体验的真实感越强,底层的游戏引擎就可能越强大。

(图片来源:绝地逃生)

从专业角度来说,游戏引擎是指一些已编写好的可编辑计算机游戏系统或者一些交互式实时图像应用程序的核心组件。这些系统为游戏设计者提供编写游戏所需的各种工具,其目的在于让游戏设计者能容易和快速地写出游戏程序而不用从零开始。


不管游戏是怎样的形式(是角色扮演游戏、即时策略游戏、冒险解谜游戏或是动作射击游戏),哪怕是一个只有1兆的小游戏,也需要游戏引擎技术。


引擎相当于游戏的框架,框架打好后,关卡设计师、建模师、动画师可往里填充内容,对于开发游戏来说乃重中之重。


当然,勇于迎难而上的人并不少,在兴趣的驱动下,学习游戏引擎技术开发并非遥不可及的事。那今天先给大家讲一下游戏引擎开发路上的第一个拦路虎——数学算法


基本数学


从 VSMath 这个文件说起


该文件里封装了 C 语言常用的数学函数,以及大量的宏定义。

以下几个宏定义是用来判断精度的,尤其是判断两个浮点数是否相等,因为绝对的相等是没有的,误差必须考虑进去。

#define VSFRONT 0
#define VSBACK 1
#define VSON 2
#define VSCLIPPED 3
#define VSCULLED 4
#define VSVISIBLE 5
#define VSINTERSECT 3
#define VSOUT 4
#define VSIN 5
#define VSNOINTERSECT 6
//弧度和角度转换函数
inline VSREAL RadianToAngle(VSREAL Radian){
 return ( Radian * 180.0f ) / VSPI ;
}
inline VSREAL AngleToRadian(VSREAL Angle){
 return ( Angle * VSPI ) / 180.0f;
}
//判断是否为 2
N
inline bool IsTwoPower(unsigned int uiN){
 return !(uiN & (uiN - 1));
}
inline unsigned short FloatToHalf(VSREAL Value)
inline VSREAL HalfToFloat(unsigned short Value)
inline unsigned int CompressUnitFloat(VSREAL f, unsigned int Bit = 16)
inline unsigned int CompressFloat(VSREAL f, VSREAL Max , VSREAL Min ,
 unsigned int Bit = 16)
inline VSREAL DecompressUnitFloat(unsigned int quantized,unsigned int Bit = 16)
inline VSREAL DecompressFloat(unsigned int quantized,VSREAL Max ,
 VSREAL Min ,unsigned int Bit = 16)
//计算正弦和余弦查找表,加快正弦和余弦计算速度
for (unsigned int i = 0 ; i <= 360 ; i++){
 VSREAL iRadian = AngleToRadian(VSREAL(i));
 FastSin[i] = SIN(iRadian);
 FastCos[i] = COS(iRadian);
}
inline VSREAL VSMATH_API GetFastSin(unsigned int i);
inline VSREAL VSMATH_API GetFastCos(unsigned int i);
VSREAL GetFastSin(unsigned int i){
 return FastSin[i];
}
VSREAL GetFastCos(unsigned int i){
 return FastCos[i];
}


下面两个函数通过给定长度的数据来计算出一个哈希索引,返回值为 32 位的哈希。

如果提供的数据量很大,可能会存在冲突,即两个不同的数据返回同一个值。

对于引擎而言,相关的数据都不多,所以冲突为 0。

void VSInitCRCTable()
unsigned int CRC32Compute( const void *pData, unsigned int uiDataSize )


最后介绍 SSE(Streaming SIMD Extensions,其中 SIMD 是 Single Instruction Multiple Data 缩写,表示单指令多数据)指令加速数学库。

本书用到该库的两个版本,一个是汇编版,另一个是“高级语言”版,高级语言版比汇编版用起来方便。


VSFastFunction 文件里面用到的是汇编 SSE 库,在 VSVector3、VSMatrix3X3、VSMatrix4X4 文件中用到的高级语言版 SSE 库,较容易理解。

常规的加法指令一次只能完成一次加法运算,而 SSE 库中的加法指令一次最多可以完成 4 次加法运算。下面分别给出 SSE 库的汇编版和高级语言版。

//汇编版 SSE 库
void VSFastAdd(const VSMatrix3X3W & InM1,const VSMatrix3X3W & InM2,
 VSMatrix3X3W & OutM)
{
 //VS 支持内嵌汇编
 __asm
 {
 mov eax, [InM2];
 mov ecx, [InM1];
 movups xmm4, [eax];
 movups xmm5, [eax+16];
 movups xmm6, [eax+32];
 movups xmm7, [eax+48];
 mov eax, [OutM];
 movups xmm0, [ecx];
 movups xmm1, [ecx+16];
 movups xmm2, [ecx+32];
 movups xmm3, [ecx+48];
 addps xmm0, xmm4;
 movups [eax], xmm0;
 addps xmm1, xmm5;
 movups [eax+16], xmm1;
 addps xmm2, xmm6;
 movups [eax+32], xmm2;
 addps xmm3, xmm7;
 movups [eax+48], xmm3;
 }
}
//高级语言版 SSE 库
void VSMatrix3X3W::operator -=(VSREAL f)
{
 __m128 _v1 = _mm_set_ps(m[0],m[1],m[2],m[3]);
 __m128 _v2 = _mm_set_ps(m[4],m[5],m[6],m[7]);
 __m128 _v3 = _mm_set_ps(m[8],m[9],m[10],m[11]);
 __m128 _v4 = _mm_set_ps(m[12],m[13],m[14],m[15]);
 __m128 _f = _mm_set_ps(f,f,f,f);
 __m128 _r1 = _mm_sub_ps(_v1,_f);
 __m128 _r2 = _mm_sub_ps(_v2,_f);
 __m128 _r3 = _mm_sub_ps(_v3,_f);
 __m128 _r4 = _mm_sub_ps(_v4,_f);
 M[0][0] = _r1.m128_f32[3]; M[0][1] = _r1.m128_f32[2];
 M[0][2] = _r1.m128_f32[1]; M[0][3] = _r1.m128_f32[0];
 M[1][0] = _r2.m128_f32[3]; M[1][1] = _r2.m128_f32[2];
 M[1][2] = _r2.m128_f32[1]; M[1][3] = _r2.m128_f32[0];
 M[2][0] = _r3.m128_f32[3]; M[2][1] = _r3.m128_f32[2];
 M[2][2] = _r3.m128_f32[1]; M[2][3] = _r3.m128_f32[0];
 M[3][0] = _r4.m128_f32[3]; M[3][1] = _r4.m128_f32[2];
 M[3][2] = _r4.m128_f32[1]; M[3][3] = _r4.m128_f32[0];
}


基本数学单元


三维向量


VSVector3 表示三维向量,这个类既可表示一个 3D 向量,也可以表示一个点。

所以这个类提供了 3D 向量应该具有的函数和作为一个空间点应该具有的函数。

class VSMATH_API VSVector3
{
public:
 union
 {
 VSREAL m[3];
 struct
 {
 VSREAL x, y, z;
 };
 };
}


矩阵类与向量类一样,都定义了 union 类型,通过数组方式或下标方式均可以访问这个向量的类属性。

VSVector3 类中的相关函数如下。

//长度
inline VSREAL GetLength(void)const;
//长度的平方
inline VSREAL GetSqrLength(void) const;
//乘以-1
inline void Negate(void);
//单位化
inline void Normalize(void);
//叉积
inline void Cross(const VSVector3 &v1,const VSVector3 &v2);
//点积
VSREAL operator * (const VSVector3 &v)const;
//两个向量的夹角(弧度)
VSREAL AngleWith( VSVector3 &v);
//用四元数旋转向量
VSQuat operator * (const VSQuat &q)const;
//3×3 矩阵变换向量
VSVector3 operator * (const VSMatrix3X3 &m)const;
//4×4 矩阵变换向量
VSVector3 operator * (const VSMatrix3X3W &m)const;
//向量加减
void operator += (const VSVector3 &v);
void operator -= (const VSVector3 &v);
VSVector3 operator + (const VSVector3 &v)const;
VSVector3 operator - (const VSVector3 &v)const;
//向量和常量加减
void operator *= (VSREAL f);
void operator /= (VSREAL f);
void operator += (VSREAL f);
void operator -= (VSREAL f);
bool operator ==(const VSVector3 &v)const;
VSVector3 operator * (VSREAL f)const;
VSVector3 operator / (VSREAL f)const;
VSVector3 operator + (VSREAL f)const;
VSVector3 operator - (VSREAL f)const;


读者应充分理解上述函数的实现和意义,尤其点积和叉积以及与矩阵的变换。


四维向量


VSVector3W 表示四维向量,该类是在 VSVector3 上加了 w 分量,主要是为了方便与 4×4 矩阵进行运算。这对于 w 分量非 1 情况下的空间变换起了很大作用。另一个应用场景是用作颜色。

class VSMATH_API VSVector3W
typedef class VSVector3W VSColorRGBA;


以下是颜色操作的相关函数,都用于实现 32 位 DWORD 类型与 4 个 float 类型的相互转换。

DWORD GetDWARGB()const;
DWORD GetDWRGBA()const;
DWORD GetDWBGRA()const;
DWORD GetDWABGR()const;
void GetUCColor(unsigned char &R,unsigned char &G,unsigned char &B,
 unsigned char &A)const;
void CreateFromARGB(DWORD ARGB);
void CreateFromBGRA(DWORD BGRA);
void CreateFromRGBA(DWORD RGBA);
void CreateFormABGR(DWORD ABGR);


下面几个关于颜色的组合函数都用于实现 VSColorRGBA 类型和 DWORD 类型的相互转换。

inline DWORD VSDWCOLORARGB(unsigned char a, unsigned char r,
 unsigned char g,unsigned char b){
 return (DWORD)
 ((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)));
}
inline DWORD VSDWCOLORBGRA(unsigned char a, unsigned char r,
 unsigned char g,unsigned char b){
 return (DWORD)
 ((((b)&0xff)<<24)|(((g)&0xff)<<16)|(((r)&0xff)<<8)|((a)&0xff)));
}
inline DWORD VSDWCOLORRGBA(unsigned char a, unsigned char r,
 unsigned char g,unsigned char b){
 return (DWORD)
 ((((r)&0xff)<<24)|(((g)&0xff)<<16)|(((b)&0xff)<<8)|((a)&0xff)));
}
inline DWORD VSDWCOLORABGR(unsigned char a, unsigned char r,
 unsigned char g,unsigned char b){
 return (DWORD)
 ((((a)&0xff)<<24)|(((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff)));
}
inline void VSDWCOLORGetARGB(DWORD ARGB,unsigned char &a,
 unsigned char &r, unsigned char &g,unsigned char &b){
 a = (ARGB>>24) & 0xff;
 r = (ARGB>>16) & 0xff;
 g = (ARGB>>8) & 0xff;
 b = (ARGB) & 0xff;
}
inline void VSDWCOLORGetBGRA(DWORD BGRA,unsigned char &a,
 unsigned char &r, unsigned char &g,unsigned char &b){
 b = (BGRA>>24) & 0xff;
 g = (BGRA>>16) & 0xff;
 r = (BGRA>>8) & 0xff;
 a = (BGRA) & 0xff;
}
inline void VSDWCOLORGetRGBA(DWORD RGBA,unsigned char &a,
 unsigned char &r, unsigned char &g,unsigned char &b){
 r = (RGBA>>24) & 0xff;
 g = (RGBA>>16) & 0xff;
 b = (RGBA>>8) & 0xff;
 a = (RGBA) & 0xff;
}
inline void VSDWCOLORGetABGR(DWORD ABGR,unsigned char &a,
 unsigned char &r, unsigned char &g,unsigned char &b){
 a = (ABGR>>24) & 0xff;
 b = (ABGR>>16) & 0xff;
 g = (ABGR>>8) & 0xff;
 r = (ABGR) & 0xff;
}


在图形渲染时,颜色需要在 unsigned char、DWORD 和 float 之间转换,不同格式表示的颜色范围不同。


3×3 矩阵


VSMatrix3X3 表示 3×3 矩阵,3×3 矩阵在变换中主要用于实现矩阵的旋转、缩放或者两者的组合。


需要提醒的是:一定要分清是左手还是右手坐标系,矩阵和向量是左乘还是右乘,矩阵是以行矩阵为主还是以列矩阵为主,并明白旋转的正方向是如何定义的。


下面是几个创建旋转矩阵的函数。

//通过一个朝向创建旋转矩阵
void CreateFromDirection(VSVector3 & Direction ,
const VSVector3 &Up = VSVector3(0,1,0));
void CreateRotX(VSREAL a); // 绕 x 轴旋转
void CreateRotY(VSREAL a); // 绕 y 轴旋转
void CreateRotZ(VSREAL a); // 绕 z 轴旋转
//绕 z 轴、x 轴和 y 轴构建欧拉角
void CreateEluer(VSREAL Roll,VSREAL Pitch, VSREAL Yaw)
void CreateAxisAngle(const VSVector3 &vAxis, VSREAL a);//绕 vAxis 旋转 a 弧度 
//通过 3 个基向量创建旋转矩阵
void CreateRot(const VSVector3 &U,const VSVector3 &V,const VSVector3 & N);以下几个函数也要了解一下。


有时在引擎里也要获取矩阵的行向量和列向量,尤其是从一个物体的旋转矩阵中得到它的前方向、上方向和右方向(3 个基向量 U、V、N)。

//按行获得向量
void GetRowVector(VSVector3 Row[3])const;
//按行、按列获得向量
void GetColumnVector(VSVector3 Column[3])const;
void GetRowVector(VSVector3 &Row0,VSVector3 &Row1,VSVector3 &Row2)const;
void GetColumnVector(VSVector3 &Column0,VSVector3 &Column1,
 VSVector3 &Column2)const;
//获得基向量
void GetUVN(VSVector3 UVN[3])const;
void GetUVN(VSVector3 & U,VSVector3 &V,VSVector3 &N)const;


有时也需要创建缩放矩阵,大部分引擎的缩放根据原点进行。

//创建缩放矩阵,根据原点缩放
void CreateScale(VSREAL fX,VSREAL fY,VSREAL fZ);
//根据轴缩放
void CreateScale(const VSVector3 & Axis,VSREAL fScale);


从一个矩阵中获取旋转量和缩放量也较常用到。

void GetScale(VSVector3 & Scale)const;
void GetScaleAndRotater(VSVector3 & Scale);


下面的两个函数很少使用,本引擎用这两个函数来求点集的 OBB 包围盒。

//构造一个行向量、一个列向量
inline void CreateFromTwoVector(const VSVector3 & v1,const VSVector3 & v2);
//求特征值、特征向量
void GetEigenSystem(VSREAL EigenValue[3],VSVector3 Eigen[3])const;


矩阵的乘法以及矩阵和向量的乘法采用以下函数实现。

inline VSMatrix3X3 operator * (const VSMatrix3X3 &Matrix)const; // 矩阵相乘
inline VSVector3 operator * (const VSVector3 &vc)const; // 矩阵和向量相乘


矩阵只有相乘才有实际意义,矩阵相乘可以视为矩阵的“加法”。

听到这里很多读者可能很迷惑,相乘怎么是加法?

学过“离散数学”的人都学过群论,其中经常提到的加法只是特定范围的加法,不同的群有各自的加法。


举个例子:整数 1 的加法就是传统的加法 1 + 1 = 2 ,减法 1 1 = 0 ,也就是 1 +(−1)。

而矩阵的加法就是 M1和 M2相乘等于 M,矩阵的减法就是 M1和 M2的逆矩阵相乘,只不过这个加法不满足交换律。

矩阵中的“0”就是单位矩阵,也称为 E。

在理解了这个的基础上,引出矩阵的插值概念。

查看以下代码。

void VSMatrix3X3::LineInterpolation(VSREAL t,const VSMatrix3X3 & M1,
 const VSMatrix3X3 &M2)
{
 *this = M1 * (1.0f - t) + M2 * t;
}
void VSMatrix3X3::Slerp(VSREAL t,const VSMatrix3X3 & M1,
 const VSMatrix3X3 &M2)
{
 VSMatrix3X3 M1Transpose,Temp;
 M1Transpose.TransposeOf(M1);
 Temp = M1Transpose * M2;
 VSREAL fAnagle;
 VSVector3 Axis;
 Temp.GetAxisAngle(Axis,fAngle);
 Temp.CreateAxisAngle(Axis,fAngle * t);
 *this = M1 * Temp;
}


从本质上讲,对于旋转矩阵的插值,第一个函数的插值算法是不正确的,虽然插值公式是 M1(1.0f t) +M2t,但“+”表示矩阵相乘,所以第二个函数的插值算法才是正确的。

在第二个函数中,插值公式是 M1(M1t)1M2t,这里的 t 也不是与矩阵简单的相乘,必须把矩阵变成轴向和角度,然后把 t 和角度相乘。


4×4 矩阵


VSMatrix3X3W 表示 4×4 矩阵,该函数用得最多的还是在空间变换上。

下面是创建 4×4 矩阵,缩放和旋转都是通过 VSMatrix3X3 实现的。

//用 3*3 矩阵创建
void CreateFrom3X3(const VSMatrix3X3 & Mat);
//平移矩阵
void CreateTranslate(VSREAL dx, VSREAL dy, VSREAL dz);
void CreateTranslate(const VSVector3 & V);


下面的函数为物体创建局部变换矩阵。

如果两个物体 A、B 都在同一个空间 M 下,B 若要变换到 A 的空间下,则为 A 物体创建变换矩阵。

其中,U、V、N 为物体 A 在空间 M 下的基向量,Point 为 A 在 M 空间下的位置。


举个简单的例子:若 M 为世界空间,所有世界空间下的物体都要变换到相机空间,那么U、V、N 就是相机在世界空间下的 3 个基向量(或者轴向),Point 为相机在世界空间下的位置。

void CreateInWorldObject(const VSVector3 &U,const VSVector3 &V,const VSVector3 &
 N,const VSVector3 &Point);


公告板(billboard)是一种特殊的面片,此处不过多介绍。一般公告板有两种:一种是完全面向相机的,一般多为粒子;另一种是只能沿 y 轴旋转的,尽量面向相机方向。

//建立公告牌变换矩阵
void CreateFormBillboard(const VSVector3 &vcPos, //公告牌位置
 const VSMatrix3X3 &CameraRotMatrix, //相机或其他矩阵
 bool bAxisY); //是否只选择沿 y 轴旋转


以下几个是创建相机矩阵、透视投影矩阵、正交投影矩阵、视口矩阵的函数。

//构建相机矩阵(根据观察方向)
bool CreateFromLookDir(const VSVector3 &vcPos, //相机位置
 const VSVector3 &vcDir, //观察方向
 const VSVector3 &vcWorldUp = VSVector3(0,1,0));
//构建相机矩阵(根据目标位置)
bool CreateFromLookAt(const VSVector3 &vcPos, //相机位置
 const VSVector3 &vcLookAt, //观察位置
 const VSVector3 &vcWorldUp = VSVector3(0,1,0)); //上方向
//建立透视投影矩阵
bool CreatePerspective(VSREAL fFov , //x 方向的张角
 VSREAL fAspect, //宽高比
 VSREAL fZN , //近剪裁面
 VSREAL fZF); //远剪裁面
//建立正交投影矩阵
bool CreateOrthogonal(VSREAL fW , //宽
 VSREAL fH, //高
 VSREAL fZN , //近剪裁面
 VSREAL fZF); //远剪裁面
//建立视口矩阵
bool CreateViewPort(VSREAL fX,VSREAL fY,VSREAL fWidth,VSREAL fHeight,VSREAL fMinz,VSREAL
fMaxz);


下面几个也是常用的函数,因为在 3×3 矩阵里都有得到旋转和缩放分量的函数,所以未在 4×4 矩阵里直接提供。

inline void Identity(void); //单位矩阵
inline void TransposeOf(const VSMatrix3X3W &Matrix); //转置
inline void InverseOf(const VSMatrix3X3W & Mat); //求逆
inline VSMatrix3X3W GetTranspose()const; //转置
inline VSMatrix3X3W GetInverse()const; //求逆
inline VSVector3 GetTranslation(void)const; //得到平移量
inline void Get3X3(VSMatrix3X3 & Mat)const; //得到 3*3 部分


下面几个也是比较常用的,但 VSVector3 和 4×4 矩阵相乘是点与 4×4 矩阵相乘,表示对点的空间变换。

如果要对向量进行空间变换,应使用 Apply3X3。

inline VSMatrix3X3W operator * (const VSMatrix3X3W &Matirx)const; // 矩阵相乘
inline VSVector3 operator * (const VSVector3 &vc)const; // 矩阵和向量乘
inline VSVector3W operator * (const VSVector3W &vc)const; // 矩阵和向量乘
//应用 3*3 的部分
inline VSVector3 Apply3X3(const VSVector3 &v)const;
//应用平移
inline VSVector3 ApplyTranlate(const VSVector3 &Point)const;


四元数


VSQuat 表示四元数类。下面的函数创建四元数。

//通过旋转轴和旋转角构造四元数
void CreateAxisAngle(const VSVector3& Axis,VSREAL fAngle);
//由欧拉角构造四元数
void CreateEule(VSREAL fRoll, VSREAL fPitch, VSREAL fYaw);


通过旋转矩阵得到四元数的函数写在了 VSMatrix3X3 里面。

//得到欧拉角
void GetEulers(VSREAL &fRoll, VSREAL &fPitch, VSREAL &fYaw)const;
//从四元数得到变换矩阵
void GetMatrix(VSMatrix3X3 &Matrix)const;
//取得旋转轴和旋转角
void GetAxisAngle(VSVector3 & Axis , VSREAL & fAngle)const;


四元数的一些常用函数如下。

//单位化
void Normalize();
//求共轭
VSQuat GetConjugate()const;
//得到长度
VSREAL GetLength(void)const;
//求逆
VSQuat GetInverse()const;
//求点积
VSREAL Dot(const VSQuat& q)const;
//求共轭
VSQuat operator ~(void) const;
//求幂
VSQuat Pow(VSREAL exp)const;
//求以 e 为底的对数
VSQuat Ln()const;
//求以 e 为底的指数
VSQuat Exp()const;
void operator /= (VSREAL f);
VSQuat operator / (VSREAL f)const;
void operator *= (VSREAL f);
VSQuat operator * (VSREAL f)const;
VSQuat operator * (const VSVector3 &v) const;
VSQuat operator * (const VSQuat &q) const;
void operator *= (const VSQuat &q);
void operator += (const VSQuat &q);
VSQuat operator + (const VSQuat &q) const;
void operator -= (const VSQuat &q);
VSQuat operator - (const VSQuat &q) const;
bool operator ==(const VSQuat &q)const;


四元数旋转的示例代码如下。

//求 q2 绕 q1 旋转后的四元数
void Rotate(const VSQuat &q1, const VSQuat &q2);
//旋转一个向量
VSVector3 Rotate(const VSVector3 &v)const;


四元数插值的相关实现可参考《3D 数学基础:图形与游戏开发》一书。

//插值
void Slerp(VSREAL t,const VSQuat & q1,const VSQuat & q2);
//三角形二维球型插值
void TriangleSlerp(VSREAL t1,VSREAL t2, const VSQuat & q1,const VSQuat & q2,
 const VSQuat & q3);
//四元数样条插值
void Slerp(VSREAL t,const VSQuat & q1,const VSQuat & q2,const VSQuat & s1,
 const VSQuat & s2);
void SlerpSValueOf(const VSQuat & q1,const VSQuat & q2,const VSQuat & q3);


基本图形单元


这一节主要介绍引擎中常用的基本图元,相机裁剪、射线检测、物体碰撞等都与它们密切相关,每一个图元是一个类,类的属性是根据空间几何定义来封装的,类的方法也很容易分类,主要是与其他图元的位置关系,或者与其他图元的距离判定(《计算机图形学几何工具算法详解》一书中有详细的算法描述)。

下图展示了图元类的继承关系。



点用 VSVector3 类表示。


直线、射线、线段


VSLine3 表示直线,直线定义为一个点和一个方向,与射线的区别为直线的方向可以为负方向,这个方向一定是单位化的。

P = P0 + tDir

上面的等式是直线的参数化方程,t 为参数。

给定 t,就可以算出 P;给定 P,则可以算出 t。

//给定点 P,求 t
bool GetParameter(const VSVector3 &Point,VSREAL &fLineParameter )const;
//构建直线
inline void Set(const VSVector3 & Orig,const VSVector3 &Dir);
//得到 P0 和 dir
inline const VSVector3 & GetOrig()const;
inline const VSVector3 & GetDir()const;
//给定 t,求 P
inline VSVector3 GetParameterPoint(VSREAL fLineParameter)const;


下面的函数用于判断直线与其他图元的位置关系。

//判断直线与三角形的位置关系。bCull 为是否为背面剪裁,是否考虑朝向,t 返回相交长度
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSTriangle3 & Triangle, bool bCull,VSREAL &fLineParameter,
 VSREAL fTriangleParameter[3])const;
//判断直线与平面的位置关系
//VSNOINTERSECT VSNTERSECT VSON VSBACK VSFRONT
int RelationWith(const VSPlane3 &Plane, bool bCull,VSREAL &fLineParameter)const;
//判断直线与矩形的位置关系
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSRectangle3 &Rectangle,bool bCull,VSREAL &fLineParameter,
 VSREAL fRectangleParameter[2])const;
//判断直线与球的位置关系
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSSphere3 &sphere, unsigned int &Quantity,VSREAL &tNear,
 VSREAL &tFar)const;
//判断直线与 OBB 的位置关系
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSOBB3 &OBB, unsigned int &Quantity,VSREAL &tNear,VSREAL
 &tFar)const;
//判断直线与 AABB 的位置关系
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSAABB3 &AABB, unsigned int &Quantity,VSREAL &tNear,VSREAL &
 tFar)const;
//判断直线与多边形的位置关系
//VSNOINTERSECT VSNTERSECT
int RelationWith(const VSPolygon3 &Polygon,VSREAL &fLineParameter, bool bCull,int &
 iIndexTriangle,VSREAL fTriangleParameter[3])const;


下面的函数用于计算直线与其他图元的距离。

//计算点到直线的距离
VSREAL SquaredDistance(const VSVector3 &Point,VSREAL &fLineParameter)const;
//计算直线和直线的距离
VSREAL SquaredDistance(const VSLine3 &Line,VSREAL &fLine1Parameter,VSREAL &fLine2
 Parameter)const;
//计算直线和射线的距离
VSREAL SquaredDistance(const VSRay3 &Ray,VSREAL &fLineParameter,VSREAL &
 fRayParameter)const;
//计算直线和线段的距离
VSREAL SquaredDistance(const VSSegment3 & Segment,VSREAL &fLineParameter,VSREAL &
 fSegmentParameter)const;
//计算直线和三角形的距离
VSREAL SquaredDistance(const VSTriangle3& Triangle,VSREAL &fLineParameter,VSREAL
 fTriangleParameter[3])const;
//计算直线和矩形的距离
VSREAL SquaredDistance(const VSRectangle3& Rectangle,VSREAL &fLineParameter,VSREAL
 fRectangleParameter[2])const;
//计算直线和 OBB 的距离
VSREAL SquaredDistance(const VSOBB3 & OBB,VSREAL &fLineParameter,VSREAL
 fOBBParameter[3])const;
//计算直线和球的距离
VSREAL Distance(const VSSphere3 &Sphere,VSREAL &fLineParameter,VSVector3 &
 SpherePoint)const;
//计算直线和平面的距离
VSREAL Distance(const VSPlane3 &Plane,VSVector3 &LinePoint,VSVector3 &PlanePoint)
 const;
//计算直线和 AABB 的距离
VSREAL SquaredDistance(const VSAABB3 &AABB,VSREAL &fLineParameter, VSREAL
 fAABBParameter[3])const;
//计算直线和多边形的距离
VSREAL SquaredDistance(const VSPolygon3 & Polygon,VSREAL &fLineParameter,int&
 IndexTriangle,VSREAL fTriangleParameter[3])const;


VSRay3 表示射线,射线和直线的唯一区别在于 t 可以为负。

VSSegment3 表示线段,线段不同于直线,它有端点属性。

对于 P = P0 + tDir 来说,t 是有范围的,所以它的定义方式有两种:第一种基于两个点,第二种基于方向和长度。

inline void Set(const VSVector3 &Orig,const VSVector3 &End);
inline void Set(const VSVector3 &Orig,const VSVector3 &Dir,VSREAL fLen);


平面、三角形、矩形、多边形


VSPlane 表示平面,平面的参数化方程为 N (P0 – P1) = 0。

平面的法线垂直于平面上所有的线,简化为NP0 + D = 0,D = NP1为常数;所有平面上的点 P 都满足 NP + D = 0。

下面几个函数都可以创建一个平面。

//通过平面法向量和平面上一点确定一个平面
inline void Set(const VSVector3 &N, const VSVector3 &P);
//通过平面法向量和 D 确定一个平面
inline void Set(const VSVector3 &N , VSREAL fD);
//通过 3 个点确定一个平面
inline void Set(const VSVector3 &P0, const VSVector3 &P1, const VSVector3 &P2);
inline void Set(const VSVector3 Point[3]);


下面的函数用于计算平面与其他图元的距离。

//计算点到平面的距离
VSREAL Distance(const VSVector3 &Point,VSVector3 &PlanePoint)const;
//计算平面和球的距离
VSREAL Distance(const VSSphere3 &Sphere,VSVector3 & SpherePoint)const;
//计算直线和平面的距离
VSREAL Distance(const VSLine3 &Line,VSVector3 &PlanePoint, VSVector3 &LinePoint)const;
//计算射线和平面的距离
VSREAL Distance(const VSRay3 & Ray,VSVector3 &PlanePoint,VSVector3 &RayPoint)const;
//计算线段和平面的距离
VSREAL Distance(const VSSegment3 & Segment,VSVector3 &PlanePoint, VSVector3 &
Segment Point)const;
//计算平面和平面的距离
VSREAL Distance(const VSPlane3 &Plane,VSVector3 &Plane1Point,VSVector3 & Plane2Point)
const;
//计算平面和三角形的距离
VSREAL Distance(const VSTriangle3 &Triangle,VSVector3 &PlanePoint, VSVector3 &
Triangle Point)const;
//计算矩形和平面的距离
VSREAL Distance(const VSRectangle3 &Rectangle,VSVector3 &PlanePoint,VSVector3 &
Rectangle Point)const;
//计算 OBB 和平面的距离
VSREAL Distance(const VSOBB3& OBB,VSVector3 &PlanePoint,VSVector3 & OBBPoint)const;
//计算 AABB 和平面的距离
VSREAL Distance(const VSAABB3 &AABB,VSVector3 &PlanePoint,VSVector3 & AABBPoint)const;
//计算平面和多边形的距离
VSREAL Distance(const VSPolygon3 &Polygon,VSVector3 &PlanePoint,int& IndexTriangle,
VSVector3 &TrianglePoint)const;


下面的函数用于判断平面与其他图元的位置关系。

//判断点和平面的位置关系
//VSFRONT VSBACK VSPLANAR
int RelationWith(const VSVector3 &Point)const;
//判断直线和平面的位置关系
/VSNOINTERSECT VSNTERSECT VSON VSBACK VSFRONT
int RelationWith(const VSLine3 &Line, bool bCull,VSREAL &fLineParameter)const;
//判断射线和平面的位置关系
//VSNOINTERSECT VSNTERSECT VSON VSBACK VSFRONT
int RelationWith(const VSRay3 &Ray, bool bCull,VSREAL &fRayParameter)const;
//判断线段和平面的位置关系
//VSNOINTERSECT VSNTERSECT VSON VSBACK VSFRONT
int RelationWith(const VSSegment3 &Segment, bool bCull,VSREAL &fSegmentParameter)
const;
//判断平面和 OBB 的位置关系
//VSFRONT VSBACK VSINTERSECT
int RelationWith(const VSOBB3 &OBB)const;
//判断平面和 AABB 的位置关系
//VSFRONT VSBACK VSINTERSECT
int RelationWith(const VSAABB3 &AABB)const;
//判断平面和球的位置关系
//VSFRONT VSBACK VSINTERSECT
int RelationWith(const VSSphere3 &Sphere)const;
//判断平面和三角形的位置关系
//VSON VSFRONT VSBACK VSINTERSECT
int RelationWith(const VSTriangle3 &Triangle)const;
int RelationWith(const VSTriangle3 &Triangle ,VSSegment3 & Segment)const;
//判断参数平面和平面的位置关系
//VSNOINTERSECT VSINTERSECT
int RelationWith(const VSPlane3 &Plane)const;
int RelationWith(const VSPlane3 &Plane,VSLine3 &Line)const;
//判断平面和矩形的位置关系
//VSON VSFRONT VSBACK VSINTERSEC
Tint RelationWith(const VSRectangle3 & Rectangle)const;
int RelationWith(const VSRectangle3 &Rectangle,VSSegment3 &Segment)const;
//判断平面和多边形的位置关系
//VSON VSFRONT VSBACK VSINTERSECTint RelationWith(const VSPolygon3 &Polygon)const;
int RelationWith(const VSPolygon3 &Polygon,VSSegment3 & Segment)const;
//判断平面和圆柱的位置关系
int RelationWith(const VSCylinder3 &Cylinder3)const;


VSTriangle3 表示三角形类,三角形类从平面类派生而来,它的构造方式很简单,就是 3 个点。

P=P1U+P2V+P3(1 U V)是三角形的参数化方程。

给定一个点 P,参数 U、V 可以通过公式推导出来;给定参数 U、V,P 也可以推导出来。

bool GetParameter(const VSVector3 &Point,VSREAL fTriangleParameter[3])const;
inline VSVector3 GetParameterPoint(VSREAL fTriangleParameter[3])const;


VSRectangle3 表示矩形类,矩形也是从平面类派生而来的,它由两个垂直向量和一个点定义。


球体、有向包围盒、立方体


VSSphere3 表示球体,VSOBB3 表示有向包围盒,VSAABB3 表示立方体。因为没有实现物理引擎,所以圆柱体、胶囊体和椭球体等都没单独实现。在引擎里面球体和立方体用得最多,都用在场景管理里。除了这些之外,还有一些合并算法,球体与球体合并,立方体与立方体合并。


不同游戏引擎的内部架构千差万别,而且游戏引擎涉及的知识点甚多,很少有人能全面把握每一个知识细节。


同时,游戏引擎属于实践性的工程,必须有足够令人信服的演示示例以及代码支持,加上商用引擎的授权、作者个人时间有限等各方面的因素,导致市面上的游戏引擎图书要么泛泛而谈,要么距离真正开发游戏相去甚远。


如果你还想了解游戏引擎的基本原理和作用、了解大公司开发引擎的基本流程、 获得良好的游戏引擎方面的知识储备,这本《 游戏引擎原理与实践 卷1 基础框架》推荐给大家, 本书专门配套的引擎和示例,可以了解开发游戏引擎的具体细节。

本书是腾讯游戏引擎设计师程东哲基于多年经验和积累的力作,详尽示例,诠释游戏引擎制作与开发技术,Milo等游戏业内资-深专家鼎力推荐。


关注”异步图书"微信号,回复:“51807” ,下载本书配套资源。


-END-

本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.souzhinan.com/yx/350250.html