D3D11地形渲染教程七之HeightBasedMovement(利用四叉树加速查询)

来源:互联网 发布:云计算 维基百科 编辑:程序博客网 时间:2024/05/15 23:51

上一节教程D3D11进阶教程六之四叉树(QuadTree) 我们用四叉树来对加速对可见区域的查询(FrustumCull),进而对地形渲染进行优化,这一节教程利用四叉树加速查询的特点,加速计算HeightBasedMovement的过程.


程序的结构如下:




一,HeightBasedMovement(基于高度的运动)是什么?

这里我借用一下虚幻四引擎做的演示:




一般游戏场景呢,我们的游戏人物是站在一块地上的(当然上面图仅为demo演示,并不说明虚幻四是基于HeightBasedMovement),那么在游戏中我们怎么知道我们的人物在移动的过程中是站在一块地上的呢,方法其实很简单: 保证物在移动过程的每时每刻其位置的Y值始终跟其所在的三角形的Y值一样大或者稍微大一点点。

上面这段描述也许很不清晰,因为很多概念不怎么好描述,下面我用图来进一步说明:




画工水平有点差,请见谅,上面图中展示的我们用线框模式渲染的地形图,图中红色的为游戏人物,我们将这个游戏人物的位置当作一个点,作出一条以(0,-1,0)也就是Y轴负方向为方向向量,经过游戏人物这个点的直线,这条直线与构成地形的某个三角形相交于点P,这个P点坐标的Y值就可以作为游戏人物的位置的Y值了,这样人物看起来就是站在地形上面了。

那么我们如何计算那条直线和地形的交点呢?也许你会说我们可以直接遍历对所有的三角形求交点,简单粗暴,但是简单粗暴往往意味着效率低下,在前面的教程我们指出这个地形的总共的三角形数量有130000左右,直接遍历程序运行效率将很低,所以我们还是用前面教程的四叉树加速查询直线和三角形的求交。

加速的过程如下:判断人物的位置的X,Z值(忽视Y值)是否在地形的坐标范围内,如果不在,则直线与地形无交点,如果在则递归遍历四个子树节点,最终直到找到人物的位置处于哪个叶子节点为止。假设我们找到人物的(X,Z)位置处于哪个叶子节点,我们的叶子节点存在10000个三角形,这时候我们再采用遍历的方式对这10000个三角形和直线求交,这样我们大大加快了直线和三角形的求交过程。



递归求交的代码:

void QuadTreeClass::FindNode(NodeType* node, float x, float z, float& height){float xMin, xMax, zMin, zMax;int count,index;float vertex1[3], vertex2[3], vertex3[3];bool foundHeight;//计算这个节点的代表范围xMin = node->positionX-node->width/2.0f;xMax = node->positionX + node->width / 2.0f;zMin = node->positionZ - node->width / 2.0f;zMax = node->positionZ + node->width / 2.0f;//如果这个点不处于这个范围,则退出函数if (x<xMin || x>xMax || z<zMin || z>zMax){return;}//初始化countcount = 0;//这个点处于这个节点所在范围,则继续遍历其子节点for (int i = 0; i < 4; ++i){if (node->ChildNode[i] != NULL){++count;FindNode(node->ChildNode[i], x, z, height);}}//如果这个节点不是叶子,则无需进行下面的操作,因为这些操作是留给叶子节点进行的if (count > 0){return;}//遍历这个节点所有的三角形,找到x,z是否位于这个节点的某个三角形中,并计算高度for (int i = 0; i < node->trangleCount; ++i){index = i * 3;vertex1[0] = node->vertices[index].x;vertex1[1] = node->vertices[index].y;vertex1[2] = node->vertices[index].z;++index;vertex2[0] = node->vertices[index].x;vertex2[1] = node->vertices[index].y;vertex2[2] = node->vertices[index].z;++index;vertex3[0] = node->vertices[index].x;vertex3[1] = node->vertices[index].y;vertex3[2] = node->vertices[index].z;//核对我们正在x,z位置是否在vertex1,vertex2,vertex3所组成的三角形中foundHeight = CheckHeightOfTriangle(x, z, height, vertex1, vertex2, vertex3);//找到了则退出函数if (foundHeight){return;}}return;}



二,如何求直线和三角形的交点。

这里将直线与三角形的相交分为两步:
(1)先求出直线与三角形所在平面的交点
(2)判断"(1)"求出的交点是否位于三角形内

这里分享一篇技术博客,对直线和三角形求交讲的挺好的:求空间中直线与一个与一个平面的交点并判断交点是否在某个三角形区域内部


看完上面一篇博客你就差不多就懂了,下面针对求空间中直线与一个与一个平面的交点并判断交点是否在某个三角形区域内部挑出的一个问题:为什么三角形叉乘得到的向量的模的二分之一为三角形的面积?看下面:


求直线和三角形交点的代码实现:

bool QuadTreeClass::CheckHeightOfTriangle(float x, float z, float& height, float vertex1[3], float vertex2[3], float vertex3[3]){/*第一步,先求出经过点(x,0,z),并以(0,-1,0)为方向向量的直线与(经过vertex1,vertex2,vertex3)平面的交点*///向量AB和向量AC,平面法向量float AB[3];float AC[3];float normal[3];AB[0] = vertex2[0] - vertex1[0];AB[1] = vertex2[1] - vertex1[1];AB[2] = vertex2[2] - vertex1[2];AC[0] = vertex3[0] - vertex1[0];AC[1] = vertex3[1] - vertex1[1];AC[2] = vertex3[2] - vertex1[2];normal[0] = AB[1] * AC[2]- AB[2] * AC[1];normal[1] = AB[2] * AC[0]- AB[0] * AC[2];normal[2] = AB[0] * AC[1]- AB[1] * AC[0];//交点坐标float InsecPosX,InsecPosY,InsecPosZ;//直线方程参数t,P=Po+t*LineDir;float t;//利用公式求出tt = ((vertex1[0] - x)*normal[0] + (vertex1[1] - 0)*normal[1] + (vertex1[2] - z)*normal[2]) / (normal[0] * 0 + normal[1] * (-1) + normal[2] * 0);//求出交点的坐标InsecPosX = x + t * 0;InsecPosY = 0 + t*(-1);InsecPosZ = z + t * 0;/*第二步,已经顶点vertex1,vertex2,vertex3构成一个三角形,并且InsecPos与vertex1,vertex2,vertex3共面,则求出InsecPos是否在三角形内,用面积法*/float vertex4[3];float area1, area2, area3,area4,areaAll;vertex4[0] = InsecPosX;vertex4[1] = InsecPosY;vertex4[2] = InsecPosZ;//计算vertex1,vertex2,vertex4构成三角形的面积area1 = GetTriangleArea(vertex1, vertex2, vertex4);//计算vertex1,vertex3,vertex4构成三角形的面积area2= GetTriangleArea(vertex1, vertex3, vertex4);//计算vertex2,vertex2,vertex4构成三角形的面积area3 = GetTriangleArea(vertex2, vertex3, vertex4);//计算vertex1,vertex2,vertex3构成三角形的面积area4= GetTriangleArea(vertex1, vertex2, vertex3);areaAll = area1 + area2 + area3;//如果面积误差在0.0001之内,则代表其(x,0,z)与三角形所在面的交点位于三角形内if (fabsf(areaAll - area4) < 0.001f){height = InsecPosY;return true;}return false;}float QuadTreeClass::GetTriangleArea(float vertex1[3], float vertex2[3], float vertex3[3]){//求vertex1,vertex2,vertex3形成平面的法向量float AB[3];float AC[3];float normal[3];float m; float area;AB[0] = vertex2[0] - vertex1[0];AB[1] = vertex2[1] - vertex1[1];AB[2] = vertex2[2] - vertex1[2];AC[0] = vertex3[0] - vertex1[0];AC[1] = vertex3[1] - vertex1[1];AC[2] = vertex3[2] - vertex1[2];normal[0] = AB[1] * AC[2] - AB[2] * AC[1];normal[1] = AB[2] * AC[0] - AB[0] * AC[2];normal[2] = AB[0] * AC[1] - AB[1] * AC[0];//计算法向量的模m = sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);//计算三角形的面积area = m / 2.0f;return area;}




实现第一人称相机移动的代码:

bool GraphicsClass::Frame(int CPU,int FPS){bool result;bool foundHeight;float height;float DeltaTime;int mouseXOffset, mouseYOffset;static float rotateY = 0.0f;//计算每帧的时间mTimerClass->Frame();DeltaTime = mTimerClass->GetTime();//更新mText的内容(CPU和FPS)result = mTextClass->SetCpuUsage(CPU, mD3D->GetDeviceContext());if (!result){return false;}result = mTextClass->SetFPS(FPS, mD3D->GetDeviceContext());if (!result){return false;}//进行InputClass对象的帧函数result = mInputClass->Frame();if (!result){return false;}mInputClass->GetMousePositionOffset(mouseXOffset, mouseYOffset);//鼠标右键处于按下的状态才能进行(左右移动)(前后移动)(旋转的操作)if (mInputClass->IsMouseRightButtuonPressed()&&FPS>=5&&FPS<=1000000){//"W","S"键操作if (mInputClass->IsWPressed()){mFirstCameraClass->Walk(0.013f*DeltaTime);}else if (mInputClass->IsSPressed()){mFirstCameraClass->Walk(-0.013f*DeltaTime);}//"A","D"键操作if (mInputClass->IsAPressed()){mFirstCameraClass->Strafe(-0.012f*DeltaTime);}else if (mInputClass->IsDPressed()){mFirstCameraClass->Strafe(0.012f*DeltaTime);}//"Q","E"键操作if (mInputClass->IsQPressed()){mFirstCameraClass->UpDown(-0.010f*DeltaTime);}else if (mInputClass->IsEPressed()){mFirstCameraClass->UpDown(0.010f*DeltaTime);}//进行视角上下的旋转(跟刚开始的旋转角度在正负90度之间)if (rotateY <= 90.0f&&rotateY >= -90.0f){rotateY += (float)mouseYOffset*0.007f*DeltaTime*(float)FPS / 4000.0f;mFirstCameraClass->Pitch((float)mouseYOffset*0.007f*DeltaTime*(float)FPS / 4000.0f);}//进行视角左右的旋转mFirstCameraClass->RotateY((float)mouseXOffset*0.007f*DeltaTime*(float)FPS/4000.0f);}//利用HeightBasedMovement技术来求相机的位置的Y值XMFLOAT3 cameraPos = mFirstCameraClass->GetPosition();foundHeight = mQuadTreeClass->GetHeightAtPosition(cameraPos.x, cameraPos.z, height);if (foundHeight){if (cameraPos.y<(height+2.0)){mFirstCameraClass->SetPosition(XMFLOAT3(cameraPos.x,height+2.0f,cameraPos.z));}}//如果按下ESC,则破坏窗口if (mInputClass->IsEscapePressed()){DestroyWindow(mhwnd);}return true;}




程序运行,第一人称相机紧紧贴着地面移动:






源代码链接:
http://download.csdn.net/detail/qq_29523119/9728546


0 0
原创粉丝点击