Half Life2 Displacement Terrain System
HL2的地形系统是一个另类的地形系统,它没有使用当前流行的heightmap技术,而是完全使用displacement map技术来进行处理,使用displacement map技术的初衷是为了在框架中和BSP技术保持一致,这是因为在BSP中最小的渲染图元是surface,surface的限制是所有的顶点必须位于同一个平面上,实际上从理论上来说在HL2这样的引擎中也可以使用heightmap来构造地形,那就是将heightmap数据也分块保存,但是当前流行的地形技术使用的都是一个整张heightmap,而且大量的LOD算法都是基于这个基础上的。使用displacement map的另外一个好处是可以构造更加复杂的地形,这是因为它存在法线的缘故。同时使用displacement map还有一个好处是在将来,因为当前MS已经对displacement map技术进行了支持,现在的问题是GPU在硬件上还不支持,如果一旦硬件支持的话,就有可能将整个地形由GPU来处理,那么对地形渲染的速度和精度会有非常大的提高。
1、what’s Displacement Map
displacemtn map技术是最近几年才出现的一个技术,它出现的初衷是为了解决内存和GPU之间的带宽问题,它开始主要用于高poly角色模型的渲染,由于模型的高数据量会使内存和GPU之间的传输成为瓶颈,因此将其转换为低poly模型和一张displacement传送到GPU中,在GPU中转换为高poly模型进行渲染,可以大大减轻传送的数据量。
但是当前这项技术已经获得非常大的发展,它使用的范围非常大,从室外地形、水面、海洋到室内场景的渲染,它为提高场景的真实度提供了一个有力的工具。一个displacement map通常包含两部分:一张normal map用来保存法线,另一张height map用来保存顶点的偏移。当需要从低poly模型转换为高poly模型时很简单,首先从模型的一个face上获得一个插值点P1,然后从displacement map中获得相应位置的normal和distance,那么新的顶点P2为:
P2 = P1 + normal*distance
关于displacement map的参考文章请看shaderX2中的Displacement Mapping。
2、HL2 Displacement Map Rule
在HL2中displacement map信息被保存在BSP文件中,它有以下几个数据块:
LUMP_DISP_VERTS 保存displacement真实顶点信息
LUMP_DISP_TRIS 保存displacement三角形标识符信息(walkable or buildable)
LUMP_DISPINF 保存displacement 的连接信息
它们保持的信息结构如下:
LUMP_DISP_VERTS:
class CDispVert
{
public:
Vector m_vVector;// Vector field defining displacement volume.
float m_flDist; // Displacement distances.
float m_flAlpha;// "per vertex" alpha values.
};
LUMP_DISP_TRIS:
class CDispTri
{
public:
unsigned short m_uiTags; // Displacement triangle tags.
};
LUMP_DISPINF:
class ddispinfo_t
{
public:
int NumVerts() const{ return NUM_DISP_POWER_VERTS(power); }
intNumTris() const{ return NUM_DISP_POWER_TRIS(power); }
public:
Vector startPosition;// start position used for //orientation -- (added BSPVERSION 6)
int m_iDispVertStart;// Index into LUMP_DISP_VERTS.
int m_iDispTriStart;// Index into LUMP_DISP_TRIS.
int power; // power - indicates size of map //(2^power + 1)
int minTess; // minimum tesselation allowed
float smoothingAngle; // lighting smoothing angle
int contents; // surface contents
unsigned short m_iMapFace; // Which map face this
//displacement comes from.
int m_iLightmapAlphaStart;// Index into ddisplightmapalpha.
int m_iLightmapSamplePositionStart;// Index into.
// LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS
CDispNeighbor m_EdgeNeighbors[4];// Indexed by NEIGHBOREDGE_ defines.
CDispCornerNeighbors m_CornerNeighbors[4]; // Indexed by CORNER_ defines.
enum { ALLOWEDVERTS_SIZE = PAD_NUMBER( MAX_DISPVERTS, 32 ) / 32 };
unsigned long m_AllowedVerts[ALLOWEDVERTS_SIZE]; //
// This is built based on the layout and
//sizes of our neighbors
// and tells us which
//vertices are allowed to be active.
};
在HL2中规定一个displacement最大由17*17个顶点组成,每一个displacement都将左下角看作是自身坐标系的原点,并按照顺时针方向进行记数。如下: