腾讯游戏开发者是谁(腾讯资深程序员解密游戏引擎开发)(1)

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

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

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

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

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

游戏引擎到底是什么呢?

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

腾讯游戏开发者是谁(腾讯资深程序员解密游戏引擎开发)(2)

(图片来源:绝地逃生)

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

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

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

腾讯游戏开发者是谁(腾讯资深程序员解密游戏引擎开发)(3)

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

基本数学

从 VSMath 这个文件说起

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

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

#defineVSFRONT0 #defineVSBACK1 #defineVSON2 #defineVSCLIPPED3 #defineVSCULLED4 #defineVSVISIBLE5 #defineVSINTERSECT3 #defineVSOUT4 #defineVSIN5 #defineVSNOINTERSECT6 //弧度和角度转换函数 inlineVSREALRadianToAngle(VSREALRadian){ return(Radian*180.0f)/VSPI; } inlineVSREALAngleToRadian(VSREALAngle){ return(Angle*VSPI)/180.0f; } //判断是否为2 N inlineboolIsTwoPower(unsignedintuiN){ return!(uiN&(uiN-1)); } inlineunsignedshortFloatToHalf(VSREALValue) inlineVSREALHalfToFloat(unsignedshortValue) inlineunsignedintCompressUnitFloat(VSREALf,unsignedintBit=16) inlineunsignedintCompressFloat(VSREALf,VSREALMax,VSREALMin, unsignedintBit=16) inlineVSREALDecompressUnitFloat(unsignedintquantized,unsignedintBit=16) inlineVSREALDecompressFloat(unsignedintquantized,VSREALMax, VSREALMin,unsignedintBit=16) //计算正弦和余弦查找表,加快正弦和余弦计算速度 for(unsignedinti=0;i<=360;i ){ VSREALiRadian=AngleToRadian(VSREAL(i)); FastSin[i]=SIN(iRadian); FastCos[i]=COS(iRadian); } inlineVSREALVSMATH_APIGetFastSin(unsignedinti); inlineVSREALVSMATH_APIGetFastCos(unsignedinti); VSREALGetFastSin(unsignedinti){ returnFastSin[i]; } VSREALGetFastCos(unsignedinti){ returnFastCos[i]; }

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

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

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

voidVSInitCRCTable() unsignedintCRC32Compute(constvoid*pData,unsignedintuiDataSize)

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

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

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

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

//汇编版SSE库 voidVSFastAdd(constVSMatrix3X3W&InM1,constVSMatrix3X3W&InM2, VSMatrix3X3W&OutM) { //VS支持内嵌汇编 __asm { moveax,[InM2]; movecx,[InM1]; movupsxmm4,[eax]; movupsxmm5,[eax 16]; movupsxmm6,[eax 32]; movupsxmm7,[eax 48]; moveax,[OutM]; movupsxmm0,[ecx]; movupsxmm1,[ecx 16]; movupsxmm2,[ecx 32]; movupsxmm3,[ecx 48]; addpsxmm0,xmm4; movups[eax],xmm0; addpsxmm1,xmm5; movups[eax 16],xmm1; addpsxmm2,xmm6; movups[eax 32],xmm2; addpsxmm3,xmm7; movups[eax 48],xmm3; } } //高级语言版SSE库 voidVSMatrix3X3W::operator-=(VSREALf) { __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 向量应该具有的函数和作为一个空间点应该具有的函数。

classVSMATH_APIVSVector3 { public: union { VSREALm[3]; struct { VSREALx,y,z; }; }; }

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

VSVector3 类中的相关函数如下。

//长度 inlineVSREALGetLength(void)const; //长度的平方 inlineVSREALGetSqrLength(void)const; //乘以-1 inlinevoidNegate(void); //单位化 inlinevoidNormalize(void); //叉积 inlinevoidCross(constVSVector3&v1,constVSVector3&v2); //点积 VSREALoperator*(constVSVector3&v)const; //两个向量的夹角(弧度) VSREALAngleWith(VSVector3&v); //用四元数旋转向量 VSQuatoperator*(constVSQuat&q)const; //3×3矩阵变换向量 VSVector3operator*(constVSMatrix3X3&m)const; //4×4矩阵变换向量 VSVector3operator*(constVSMatrix3X3W&m)const; //向量加减 voidoperator =(constVSVector3&v); voidoperator-=(constVSVector3&v); VSVector3operator (constVSVector3&v)const; VSVector3operator-(constVSVector3&v)const; //向量和常量加减 voidoperator*=(VSREALf); voidoperator/=(VSREALf); voidoperator =(VSREALf); voidoperator-=(VSREALf); booloperator==(constVSVector3&v)const; VSVector3operator*(VSREALf)const; VSVector3operator/(VSREALf)const; VSVector3operator (VSREALf)const; VSVector3operator-(VSREALf)const;

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

四维向量

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

classVSMATH_APIVSVector3W typedefclassVSVector3WVSColorRGBA;

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

DWORDGetDWARGB()const; DWORDGetDWRGBA()const; DWORDGetDWBGRA()const; DWORDGetDWABGR()const; voidGetUCColor(unsignedchar&R,unsignedchar&G,unsignedchar&B, unsignedchar&A)const; voidCreateFromARGB(DWORDARGB); voidCreateFromBGRA(DWORDBGRA); voidCreateFromRGBA(DWORDRGBA); voidCreateFormABGR(DWORDABGR);

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

inlineDWORDVSDWCOLORARGB(unsignedchara,unsignedcharr, unsignedcharg,unsignedcharb){ return(DWORD) ((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))); } inlineDWORDVSDWCOLORBGRA(unsignedchara,unsignedcharr, unsignedcharg,unsignedcharb){ return(DWORD) ((((b)&0xff)<<24)|(((g)&0xff)<<16)|(((r)&0xff)<<8)|((a)&0xff))); } inlineDWORDVSDWCOLORRGBA(unsignedchara,unsignedcharr, unsignedcharg,unsignedcharb){ return(DWORD) ((((r)&0xff)<<24)|(((g)&0xff)<<16)|(((b)&0xff)<<8)|((a)&0xff))); } inlineDWORDVSDWCOLORABGR(unsignedchara,unsignedcharr, unsignedcharg,unsignedcharb){ return(DWORD) ((((a)&0xff)<<24)|(((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff))); } inlinevoidVSDWCOLORGetARGB(DWORDARGB,unsignedchar&a, unsignedchar&r,unsignedchar&g,unsignedchar&b){ a=(ARGB>>24)&0xff; r=(ARGB>>16)&0xff; g=(ARGB>>8)&0xff; b=(ARGB)&0xff; } inlinevoidVSDWCOLORGetBGRA(DWORDBGRA,unsignedchar&a, unsignedchar&r,unsignedchar&g,unsignedchar&b){ b=(BGRA>>24)&0xff; g=(BGRA>>16)&0xff; r=(BGRA>>8)&0xff; a=(BGRA)&0xff; } inlinevoidVSDWCOLORGetRGBA(DWORDRGBA,unsignedchar&a, unsignedchar&r,unsignedchar&g,unsignedchar&b){ r=(RGBA>>24)&0xff; g=(RGBA>>16)&0xff; b=(RGBA>>8)&0xff; a=(RGBA)&0xff; } inlinevoidVSDWCOLORGetABGR(DWORDABGR,unsignedchar&a, unsignedchar&r,unsignedchar&g,unsignedchar&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 矩阵在变换中主要用于实现矩阵的旋转、缩放或者两者的组合。

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

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

//通过一个朝向创建旋转矩阵 voidCreateFromDirection(VSVector3&Direction, constVSVector3&Up=VSVector3(0,1,0)); voidCreateRotX(VSREALa);//绕x轴旋转 voidCreateRotY(VSREALa);//绕y轴旋转 voidCreateRotZ(VSREALa);//绕z轴旋转 //绕z轴、x轴和y轴构建欧拉角 voidCreateEluer(VSREALRoll,VSREALPitch,VSREALYaw) voidCreateAxisAngle(constVSVector3&vAxis,VSREALa);//绕vAxis旋转a弧度 //通过3个基向量创建旋转矩阵 voidCreateRot(constVSVector3&U,constVSVector3&V,constVSVector3&N);以下几个函数也要了解一下。

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

//按行获得向量 voidGetRowVector(VSVector3Row[3])const; //按行、按列获得向量 voidGetColumnVector(VSVector3Column[3])const; voidGetRowVector(VSVector3&Row0,VSVector3&Row1,VSVector3&Row2)const; voidGetColumnVector(VSVector3&Column0,VSVector3&Column1, VSVector3&Column2)const; //获得基向量 voidGetUVN(VSVector3UVN[3])const; voidGetUVN(VSVector3&U,VSVector3&V,VSVector3&N)const;

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

//创建缩放矩阵,根据原点缩放 voidCreateScale(VSREALfX,VSREALfY,VSREALfZ); //根据轴缩放 voidCreateScale(constVSVector3&Axis,VSREALfScale);

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

voidGetScale(VSVector3&Scale)const; voidGetScaleAndRotater(VSVector3&Scale);

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

//构造一个行向量、一个列向量 inlinevoidCreateFromTwoVector(constVSVector3&v1,constVSVector3&v2); //求特征值、特征向量 voidGetEigenSystem(VSREALEigenValue[3],VSVector3Eigen[3])const;

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

inlineVSMatrix3X3operator*(constVSMatrix3X3&Matrix)const;//矩阵相乘 inlineVSVector3operator*(constVSVector3&vc)const;//矩阵和向量相乘

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

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

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

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

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

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

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

查看以下代码。

voidVSMatrix3X3::LineInterpolation(VSREALt,constVSMatrix3X3&M1, constVSMatrix3X3&M2) { *this=M1*(1.0f-t) M2*t; } voidVSMatrix3X3::Slerp(VSREALt,constVSMatrix3X3&M1, constVSMatrix3X3&M2) { VSMatrix3X3M1Transpose,Temp; M1Transpose.TransposeOf(M1); Temp=M1Transpose*M2; VSREALfAnagle; VSVector3Axis; 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矩阵创建 voidCreateFrom3X3(constVSMatrix3X3&Mat); //平移矩阵 voidCreateTranslate(VSREALdx,VSREALdy,VSREALdz); voidCreateTranslate(constVSVector3&V);

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

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

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

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

voidCreateInWorldObject(constVSVector3&U,constVSVector3&V,constVSVector3& N,constVSVector3&Point);

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

//建立公告牌变换矩阵 voidCreateFormBillboard(constVSVector3&vcPos,//公告牌位置 constVSMatrix3X3&CameraRotMatrix,//相机或其他矩阵 boolbAxisY);//是否只选择沿y轴旋转

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

//构建相机矩阵(根据观察方向) boolCreateFromLookDir(constVSVector3&vcPos,//相机位置 constVSVector3&vcDir,//观察方向 constVSVector3&vcWorldUp=VSVector3(0,1,0)); //构建相机矩阵(根据目标位置) boolCreateFromLookAt(constVSVector3&vcPos,//相机位置 constVSVector3&vcLookAt,//观察位置 constVSVector3&vcWorldUp=VSVector3(0,1,0));//上方向 //建立透视投影矩阵 boolCreatePerspective(VSREALfFov,//x方向的张角 VSREALfAspect,//宽高比 VSREALfZN,//近剪裁面 VSREALfZF);//远剪裁面 //建立正交投影矩阵 boolCreateOrthogonal(VSREALfW,//宽 VSREALfH,//高 VSREALfZN,//近剪裁面 VSREALfZF);//远剪裁面 //建立视口矩阵 boolCreateViewPort(VSREALfX,VSREALfY,VSREALfWidth,VSREALfHeight,VSREALfMinz,VSREAL fMaxz);

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

inlinevoidIdentity(void);//单位矩阵 inlinevoidTransposeOf(constVSMatrix3X3W&Matrix);//转置 inlinevoidInverseOf(constVSMatrix3X3W&Mat);//求逆 inlineVSMatrix3X3WGetTranspose()const;//转置 inlineVSMatrix3X3WGetInverse()const;//求逆 inlineVSVector3GetTranslation(void)const;//得到平移量 inlinevoidGet3X3(VSMatrix3X3&Mat)const;//得到3*3部分

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

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

inlineVSMatrix3X3Woperator*(constVSMatrix3X3W&Matirx)const;//矩阵相乘 inlineVSVector3operator*(constVSVector3&vc)const;//矩阵和向量乘 inlineVSVector3Woperator*(constVSVector3W&vc)const;//矩阵和向量乘 //应用3*3的部分 inlineVSVector3Apply3X3(constVSVector3&v)const; //应用平移 inlineVSVector3ApplyTranlate(constVSVector3&Point)const;

四元数

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

//通过旋转轴和旋转角构造四元数 voidCreateAxisAngle(constVSVector3&Axis,VSREALfAngle); //由欧拉角构造四元数 voidCreateEule(VSREALfRoll,VSREALfPitch,VSREALfYaw);

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

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

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

//单位化 voidNormalize(); //求共轭 VSQuatGetConjugate()const; //得到长度 VSREALGetLength(void)const; //求逆 VSQuatGetInverse()const; //求点积 VSREALDot(constVSQuat&q)const; //求共轭 VSQuatoperator~(void)const; //求幂 VSQuatPow(VSREALexp)const; //求以e为底的对数 VSQuatLn()const; //求以e为底的指数 VSQuatExp()const; voidoperator/=(VSREALf); VSQuatoperator/(VSREALf)const; voidoperator*=(VSREALf); VSQuatoperator*(VSREALf)const; VSQuatoperator*(constVSVector3&v)const; VSQuatoperator*(constVSQuat&q)const; voidoperator*=(constVSQuat&q); voidoperator =(constVSQuat&q); VSQuatoperator (constVSQuat&q)const; voidoperator-=(constVSQuat&q); VSQuatoperator-(constVSQuat&q)const; booloperator==(constVSQuat&q)const;

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

//求q2绕q1旋转后的四元数 voidRotate(constVSQuat&q1,constVSQuat&q2); //旋转一个向量 VSVector3Rotate(constVSVector3&v)const;

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

//插值 voidSlerp(VSREALt,constVSQuat&q1,constVSQuat&q2); //三角形二维球型插值 voidTriangleSlerp(VSREALt1,VSREALt2,constVSQuat&q1,constVSQuat&q2, constVSQuat&q3); //四元数样条插值 voidSlerp(VSREALt,constVSQuat&q1,constVSQuat&q2,constVSQuat&s1, constVSQuat&s2); voidSlerpSValueOf(constVSQuat&q1,constVSQuat&q2,constVSQuat&q3);

基本图形单元

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

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

腾讯游戏开发者是谁(腾讯资深程序员解密游戏引擎开发)(4)

点用 VSVector3 类表示。

直线、射线、线段

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

P=P0 tDir

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

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

//给定点P,求t boolGetParameter(constVSVector3&Point,VSREAL&fLineParameter)const; //构建直线 inlinevoidSet(constVSVector3&Orig,constVSVector3&Dir); //得到P0和dir inlineconstVSVector3&GetOrig()const; inlineconstVSVector3&GetDir()const; //给定t,求P inlineVSVector3GetParameterPoint(VSREALfLineParameter)const;

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

//判断直线与三角形的位置关系。bCull 为是否为背面剪裁,是否考虑朝向,t 返回相交长度 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSTriangle3&Triangle,boolbCull,VSREAL&fLineParameter, VSREALfTriangleParameter[3])const; //判断直线与平面的位置关系 //VSNOINTERSECTVSNTERSECTVSONVSBACKVSFRONT intRelationWith(constVSPlane3&Plane,boolbCull,VSREAL&fLineParameter)const; //判断直线与矩形的位置关系 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSRectangle3&Rectangle,boolbCull,VSREAL&fLineParameter, VSREALfRectangleParameter[2])const; //判断直线与球的位置关系 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSSphere3&sphere,unsignedint&Quantity,VSREAL&tNear, VSREAL&tFar)const; //判断直线与OBB的位置关系 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSOBB3&OBB,unsignedint&Quantity,VSREAL&tNear,VSREAL &tFar)const; //判断直线与AABB的位置关系 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSAABB3&AABB,unsignedint&Quantity,VSREAL&tNear,VSREAL& tFar)const; //判断直线与多边形的位置关系 //VSNOINTERSECTVSNTERSECT intRelationWith(constVSPolygon3&Polygon,VSREAL&fLineParameter,boolbCull,int& iIndexTriangle,VSREALfTriangleParameter[3])const;

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

//计算点到直线的距离 VSREALSquaredDistance(constVSVector3&Point,VSREAL&fLineParameter)const; //计算直线和直线的距离 VSREALSquaredDistance(constVSLine3&Line,VSREAL&fLine1Parameter,VSREAL&fLine2 Parameter)const; //计算直线和射线的距离 VSREALSquaredDistance(constVSRay3&Ray,VSREAL&fLineParameter,VSREAL& fRayParameter)const; //计算直线和线段的距离 VSREALSquaredDistance(constVSSegment3&Segment,VSREAL&fLineParameter,VSREAL& fSegmentParameter)const; //计算直线和三角形的距离 VSREALSquaredDistance(constVSTriangle3&Triangle,VSREAL&fLineParameter,VSREAL fTriangleParameter[3])const; //计算直线和矩形的距离 VSREALSquaredDistance(constVSRectangle3&Rectangle,VSREAL&fLineParameter,VSREAL fRectangleParameter[2])const; //计算直线和OBB的距离 VSREALSquaredDistance(constVSOBB3&OBB,VSREAL&fLineParameter,VSREAL fOBBParameter[3])const; //计算直线和球的距离 VSREALDistance(constVSSphere3&Sphere,VSREAL&fLineParameter,VSVector3& SpherePoint)const; //计算直线和平面的距离 VSREALDistance(constVSPlane3&Plane,VSVector3&LinePoint,VSVector3&PlanePoint) const; //计算直线和AABB的距离 VSREALSquaredDistance(constVSAABB3&AABB,VSREAL&fLineParameter,VSREAL fAABBParameter[3])const; //计算直线和多边形的距离 VSREALSquaredDistance(constVSPolygon3&Polygon,VSREAL&fLineParameter,int& IndexTriangle,VSREALfTriangleParameter[3])const;

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

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

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

inlinevoidSet(constVSVector3&Orig,constVSVector3&End); inlinevoidSet(constVSVector3&Orig,constVSVector3&Dir,VSREALfLen);

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

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

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

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

//通过平面法向量和平面上一点确定一个平面 inlinevoidSet(constVSVector3&N,constVSVector3&P); //通过平面法向量和D确定一个平面 inlinevoidSet(constVSVector3&N,VSREALfD); //通过3个点确定一个平面 inlinevoidSet(constVSVector3&P0,constVSVector3&P1,constVSVector3&P2); inlinevoidSet(constVSVector3Point[3]);

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

//计算点到平面的距离 VSREALDistance(constVSVector3&Point,VSVector3&PlanePoint)const; //计算平面和球的距离 VSREALDistance(constVSSphere3&Sphere,VSVector3&SpherePoint)const; //计算直线和平面的距离 VSREALDistance(constVSLine3&Line,VSVector3&PlanePoint,VSVector3&LinePoint)const; //计算射线和平面的距离 VSREALDistance(constVSRay3&Ray,VSVector3&PlanePoint,VSVector3&RayPoint)const; //计算线段和平面的距离 VSREALDistance(constVSSegment3&Segment,VSVector3&PlanePoint,VSVector3& SegmentPoint)const; //计算平面和平面的距离 VSREALDistance(constVSPlane3&Plane,VSVector3&Plane1Point,VSVector3&Plane2Point) const; //计算平面和三角形的距离 VSREALDistance(constVSTriangle3&Triangle,VSVector3&PlanePoint,VSVector3& TrianglePoint)const; //计算矩形和平面的距离 VSREALDistance(constVSRectangle3&Rectangle,VSVector3&PlanePoint,VSVector3& RectanglePoint)const; //计算OBB和平面的距离 VSREALDistance(constVSOBB3&OBB,VSVector3&PlanePoint,VSVector3&OBBPoint)const; //计算AABB和平面的距离 VSREALDistance(constVSAABB3&AABB,VSVector3&PlanePoint,VSVector3&AABBPoint)const; //计算平面和多边形的距离 VSREALDistance(constVSPolygon3&Polygon,VSVector3&PlanePoint,int&IndexTriangle, VSVector3&TrianglePoint)const;

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

//判断点和平面的位置关系 //VSFRONTVSBACKVSPLANAR intRelationWith(constVSVector3&Point)const; //判断直线和平面的位置关系 /VSNOINTERSECTVSNTERSECTVSONVSBACKVSFRONT intRelationWith(constVSLine3&Line,boolbCull,VSREAL&fLineParameter)const; //判断射线和平面的位置关系 //VSNOINTERSECTVSNTERSECTVSONVSBACKVSFRONT intRelationWith(constVSRay3&Ray,boolbCull,VSREAL&fRayParameter)const; //判断线段和平面的位置关系 //VSNOINTERSECTVSNTERSECTVSONVSBACKVSFRONT intRelationWith(constVSSegment3&Segment,boolbCull,VSREAL&fSegmentParameter) const; //判断平面和OBB的位置关系 //VSFRONTVSBACKVSINTERSECT intRelationWith(constVSOBB3&OBB)const; //判断平面和AABB的位置关系 //VSFRONTVSBACKVSINTERSECT intRelationWith(constVSAABB3&AABB)const; //判断平面和球的位置关系 //VSFRONTVSBACKVSINTERSECT intRelationWith(constVSSphere3&Sphere)const; //判断平面和三角形的位置关系 //VSONVSFRONTVSBACKVSINTERSECT intRelationWith(constVSTriangle3&Triangle)const; intRelationWith(constVSTriangle3&Triangle,VSSegment3&Segment)const; //判断参数平面和平面的位置关系 //VSNOINTERSECTVSINTERSECT intRelationWith(constVSPlane3&Plane)const; intRelationWith(constVSPlane3&Plane,VSLine3&Line)const; //判断平面和矩形的位置关系 //VSONVSFRONTVSBACKVSINTERSEC TintRelationWith(constVSRectangle3&Rectangle)const; intRelationWith(constVSRectangle3&Rectangle,VSSegment3&Segment)const; //判断平面和多边形的位置关系 //VSONVSFRONTVSBACKVSINTERSECTintRelationWith(constVSPolygon3&Polygon)const; intRelationWith(constVSPolygon3&Polygon,VSSegment3&Segment)const; //判断平面和圆柱的位置关系 intRelationWith(constVSCylinder3&Cylinder3)const;

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

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

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

boolGetParameter(constVSVector3&Point,VSREALfTriangleParameter[3])const; inlineVSVector3GetParameterPoint(VSREALfTriangleParameter[3])const;

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

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

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

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

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

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

腾讯游戏开发者是谁(腾讯资深程序员解密游戏引擎开发)(5)

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

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

-END-

,