LOD地形及相关技术研究

 该文章写于2009年夏,两年过去了,我也由一名学生成为了一名专业的游戏开发人员,以现在的角度来看当时十分的稚嫩,但是我觉得是我技术上的一个很有意义的里程碑,现在我发出来这篇文章,希望对学习相关技术的学生朋友有帮助。

    无图无真相,放一张效果图先:

 

LOD地形及相关技术研究

序言:

         如今LOD技术被广泛应用在游戏领域,因为他能节省cpu大量的运算且又能表现极为广阔的空间和场景,尤其是拥有超大场景的网络游戏,更是离不开这种技术。笔者要谈的LOD地形恰恰是LOD技术中的重要的组成部分,这种地形技术根据人的视觉观察特点,以较少的点和面表现出逼真的视觉效果。

       笔者由于工作和学习的需要最近开始接触LOD地形技术,从网上搜索了一些资料,经过两个星期的研究,终于初步的解读和完成了属于自己的LOD地形,我希望把自己的一些经验写出来同大家一起分享,希望大家多多指教。

       本篇文章主要详细介绍LOD地形技术的几个要点,以实用为主,感念和前景等内容在此不在熬述,基本思想同网上的资料大致相同,但也有一些笔者认为需要修改和改进的地方,如裂缝修补和定点渲染等。所谓仁者见仁智者见智,如果大家觉得有不妥的地方欢迎大家随时指证。

 

第一章   LOD的基本思想

LOD的特点就是可以表现更大型的地形和更高的细节而不消耗过多的运算量。笔者之前实现过一种静态地形,读取高度信息后直接将所有顶点进行计算和渲染,但该种方法所能渲染的TerrainSize(地形尺寸)是有限的,一般在1024*1024个顶点的情况下的FPS为5-10帧左右,要达到60帧的速度必须降低顶点数量在256*256左右,对稍大一点的地形来说,只能牺牲精度来换取速度。废话不多说,想必来看这篇文章的朋友都遇到过这样的问题,下面进入正题。

       首先看一下静态网格地形和LOD地形的网格对比:

LOD地形及相关技术研究

1-1静态地形网格

 

LOD地形及相关技术研究

1-2 LOD地形网格

通过比较发现静态网格地形拥有规则整齐的网格布线,而LOD地形却布满凌乱的大小不一的“方格”,这些“方格”我们称之为Node(节点)。然而实际上,Node在地形复杂和离观察点较近的地方会比较密集、尺寸也较小,在平坦和较远的地方比较稀疏、尺寸较大,这样的布局即解决了对地形精度的要求又保证了渲染顶点的数量,而且Node的尺寸会随着观察点的移动而进行改变。静态网格也有事先设定好地形复杂度的,但运行之后就不会再更改变了。

所谓LOD地形也就是拥有层次细节的地形,当然对顶点的优化不仅仅是在地形复杂度的评价。除此之外,LOD地形技术还包括高度数据、加载各种纹理等其他几个方面,笔者会在以下几章详细向大家描述。

 

第二章  高度数据存储和获取

         除非是在平原,绝大部分的真实地形都会有高低起伏。在游戏里面地形的高度信息一般是存储在一个张灰度图(HeightMap)上,灰度图中像素存储的是0-255的亮度值,我们就利用每个像素的亮度值来存储高度信息,如图2-1所示,左图是对应右边实际地形的灰度图,左图的中越亮的部分,在真实地形中对应区域的地形越高。

LOD地形及相关技术研究LOD地形及相关技术研究

 

2-1灰度图

       灰度图HeightMap一般会使用没有RGB颜色信息的图片格式,例如.rew和.tga等。

       有了与地形相关的灰度图之后,接下来下来的问题就是如何获取灰度图中的高度信息。首先设置一个unsigned char*类型的变量HeightDate,使用函数fread()将灰度图中的高度信息读出并写入HeightDate。

使用灰度图的像素的大小需要一定要与地形尺寸(TerrainSize)相匹配,例如:需要建立一个1024*1024的地形,地形TerrainSIze就为1024,灰度图的大小为TerrainSIze* TerrainSIze。注意:HeightDate的大小不是TerrainSIze* TerrainSIze,而是 (TerrainSIze+1)* (TerrainSIze+1),而且TerrainSize的一定为2n,原因会在下一章解释。

       存储和读取灰度图的数据实际上时地形算法中最基本也是最容易的一部分,不管是LOD地形还是静态网格地形都是使用这种技术,相对来说笔者到觉得制作符合要求的高度图和匹配的纹理图更有难度,不过目前市面上也有不少专门为地形制作高度图和纹理图的工具,大家可以尝试一下,笔者绘制灰度图使用的软件是L3DT。

 

L3DT是一款专门针对LOD地形技术设计的软件,可以轻松制作自己的LOD地形及导出相关的素材,官方下载:http://www.bundysoft.com/L3DT/downloads/

第三章    节点评价系统

         为了更好的对顶点进行优化,我们把LOD地形分为若干节点,划分节点的数量与大小就要通过一套节点评价系统来决定。

为了使细节可以根据要求来进行细化,所以使用树的方法来实现节点的分割,常用的方法有四叉树和二叉树分割,在本篇文章里使用的是四叉树分割,如图3-1所示。L3DT是四叉树二叉树结合使用。

LOD地形及相关技术研究

图3-1节点的四叉树分割,橙色为需要分割节点

       由于采用四叉树的分割方式,当节点最大限度分割时,其数目应该与地形尺寸相匹配,及(TerrainSize+1)*(TerrainSize+1),这也是为什么要求地形尺寸一定要为2n+1。

       一个四叉树节点需要包含顶点信息,顶点的数目与该节点的邻接节点有关,为了方便分割,顶点数目设为9个,结构如图3-2。

LOD地形及相关技术研究

图3-2  节点结构

       0顶点为中心点,1、2、3、4点为角点,5、6、7、8为边点,顶点和角点是固定的,顶点数量的变动时指边点数量的变动如图3-3。

LOD地形及相关技术研究

 

图3-3 

想象一下,节点分割的次数越多,地形就越精细,但是计算量也就越大,所以需要针对地形复杂区域的节点进行分割,较为平坦的地形则进行较少的分割。

       节点分割的评价系统大致分为两个部分,第一是节点到观察点的距离,第二是计算节点区域复杂度。

LOD地形及相关技术研究

节点离观察点越近分割次数越多,根据第一条评价原则,总结出第一条评价公式:L/d < C1,L为顶点到观察点的距离,d该节点的长度,C1为自定义的常量,C1的值约大顶点分割次数越多,如图3-4。

节点区域地形越复杂,分割的次数就越多,根据第二条评价原则,总结出第二条评价公式:L/r < C2。我们采用如图3-5中节点,首先获取到顶点h0到顶点h4的高度,如果该节点分割则还要获得子节点的高度h5到h8,然后依次比较各高度值,取出最大值Max,与最小值Min,r = Max-Min。

综上所述将评价公式1和评价公式2合并起来:L/(d×r) < C1×C2,变形得到最终公式:f = L/(d×r×C1×C2) < 1。当满足f < 1时则判断该节点需继续分割。

对节点判断是否分割之后,我们需要存储这个分割信息标志,我们设一个类型为bool[][]类型的数组变量QuadMat,数组的长度应该与节点的数目一致,即QuadMat[TerrainSize+1][TerrainSize+1],存储方式如图3-6所示,由下图也可看出为何顶点数目以及其高度数据HeightDate的大小也是(TerrainSize+1)*(TerrainSize+1)。

 

LOD地形及相关技术研究

第四章 视锥及裁剪

         在LOD地形中还有一项很重要的减少绘制顶点的方法,那就是对摄像机之外,那些我们观察不到的区域的顶点进行裁剪。这里说的裁剪并不是D3D里所说的裁剪,而是针对节点划分的控制,对于那些我们无法观察到的节点就将其分割信息设置为false。

       在这里提出视锥的概念,如图4-1(a)所示,观察者同显示器屏幕,以及延长线所经过的区域就构成了一个四面锥,我们所说的视锥实际上时这个四面锥去掉观察点到屏幕之间的部分,如图4-1(b)蓝色区域所示,在这个区域内的节点才能被我们观察到。

LOD地形及相关技术研究

 

 

       认识视锥之后就紧接着就是怎样获取视锥。首先获取投影矩阵matProj和观察矩阵matView,计算摄影和投影变换后的得到的矩阵matWorld。我们可以通过这个矩阵来一次获取去视锥的6个面Planes[6],方法参照如下一段代码段:

D3DXPLANE m_Planes[6];

    Planes[0].a = matWorld._13;//近平面

Planes[0].b = matWorld._23;

    Planes[0].c = matWorld._33;

    Planes[0].d = matWorld._43;

    D3DXPlaneNormalize(&m_Planes[0], &m_Planes[0]);

    Planes[1].a = matWorld._14 - matWorld._13; //远平面

    Planes[1].b = matWorld._24 - matWorld._23;

    Planes[1].c = matWorld._34 - matWorld._33;

    Planes[1].d = matWorld._44 - matWorld._43;

    D3DXPlaneNormalize(&m_Planes[1], &m_Planes[1]);

    Planes[2].a = matWorld._14 + matWorld._11; //左平面

    Planes[2].b = matWorld._24 + matWorld._21;

    Planes[2].c = matWorld._34 + matWorld._31;

    Planes[2].d = matWorld._44 + matWorld._41;

    D3DXPlaneNormalize(&m_Planes[2], &m_Planes[2]);

    Planes[3].a = matWorld._14 - matWorld._11; //右平面

    Planes[3].b = matWorld._24 - matWorld._21;

    Planes[3].c = matWorld._34 - matWorld._31;

    Planes[3].d = matWorld._44 - matWorld._41;

    D3DXPlaneNormalize(&m_Planes[3], &m_Planes[3]);

    Planes[4].a = matWorld._14 - matWorld._12; //顶面

    Planes[4].b = matWorld._24 - matWorld._22;

    Planes[4].c = matWorld._34 - matWorld._32;

    Planes[4].d = matWorld._44 - matWorld._42;

    D3DXPlaneNormalize(&m_Planes[4], &m_Planes[4]);

    Planes[5].a = matWorld._14 + matWorld._12; //底面

    Planes[5].b = matWorld._24 + matWorld._22;

    Planes[5].c = matWorld._34 + matWorld._32;

    Planes[5].d = matWorld._44 + matWorld._42;

    D3DXPlaneNormalize(&m_Planes[5], &m_Planes[5]);

 


   判断的节点座标(x,y,z)是否在视锥体内,就是判断该座标在六个面得那一侧。D3D中功能函数D3DXPlaneDotCoord()可以三维点与平面进行点击(a*x + b*y + c*z + d*1),根据返回结果与0进行比较,依次来进行判断,如果六个面得结果都大于0则改座标及在谁追体内。

   由于节点座标并不是一个点,而是一块由若干顶点构成的一个区域,所以以一个节点座标来判断该节点是否在是椎体内,当节点离视锥界面边缘的时候容易产生误差。为减小误差,我们把每个节点看做一个边长为节点长度的立方体,判断座标变换为判断立方体,进而转化为判断立方体的八个顶点,如图 4-2。

LOD地形及相关技术研究

 

 

第五章节点的渲染

    第三章已经介绍,每个节点都是由若干顶点组成,中心点和角点都是固定不变,边点的数量在0到4之间。

LOD地形及相关技术研究

 

在节点的每条边上是否有边点主要取决于相邻节点是否进行了分割,然而相邻节点既包括自身友节点(同一父节点分割出的四个节点层次为n)和父节点的友节点(层次为n-1),进行判别的时候也要把这两种情况区分开。如图5-1所示红色区域的节点,蓝色的顶点代表通过判断父节点的有节点而增加的边点,红色的顶点点代表通过判断自身友节点而增加的边点。使用边点的作用在于增加节点面数,以避免邻节的不同层次相点之间出现接缝。该方法经过笔者优化,可能与其他资料介绍的方法有所不同。


 

 

根据节点中顶点的分布特点,采用扇形绘制三角面,如图5-2所示,directX3D和OpenGL里面都包括这种绘制方法。

 

LOD地形及相关技术研究

    节点渲染的顺序采用递归的方法深度优先遍历整个四叉树的节点,遍历到子叶就进行渲染,判断该节点是否为子叶的标志就是观察该节点的分割信息为false。该章内容难以描述的更详细,若果感兴趣可以结合该章节的代码(由于代码过长,暂不放入本章内容)

第六章   修补裂缝

         通常我们把节点看做一个四叉树,根据分割层次的不同,节点的尺寸也不同,这样一层一层的分割下去直到分割的极限尺寸,如图6-1是第三章图3-6的四叉树层次结构。

LOD地形及相关技术研究

 

       我们所说的裂缝就是当相邻的节点,当层次大于或等于2,在两个节点之间会出现的一个三角形缝隙,如图6-2。上一章所使用的渲染节点的方法本身具有修补裂缝的功能,但只是针对层次相差为1的节点,如上所述的情况就无能为力了。

LOD地形及相关技术研究

 

如图6-3中红线两边的节点,层次刚好相差2,右节点在相邻的边上没有定点,而红线右边的两个小的节点相接处却各有一个顶点,这种情况下必然造成三角裂缝。

LOD地形及相关技术研究

 

解决裂缝的办法有很多,在这里介绍一种效率较高也是笔者正在使用的方法,就是避免相邻节点分割层次差大于等于2的情况出现。通常我们把如图5-3蓝色区域的四个节点视为不合法的节点,怎样避免这样不合法的节点出现呢?只有将该四个节点的父节点的分割信息设置为false,禁止该父节点进行分割。

通过节点评价系统和视锥获得分割信息QuadMat,为了剔除不合法的节点,需要针对该信息进行四叉树的广度优先遍历,即逐层遍历节点。如果遍历层次为n的节点,则需要判断相邻所有的层次为n的节点的分割信息,如果为true就允许分割,如果是false就不允许。该方法经过笔者优化,可能与其他资料介绍的方法有所不同。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章