Direct9学习之 - 地形
发表于2016-09-08
一. 地形的作用:
地形,不言而喻是用来描绘游戏中高山 盆地 平原等地貌,用于确定对象在游戏世界的空间高度。
二. 地形基本原理:
地形的实现是用地形网格(N行M列矩形网格)和高度图实现,地形网格用于绘制地表,高度图信息则用来表示地形高度。通过高度图的不同高度使地形形成高山盆地等。(高度图中保存了地形高度信息)
地形网格主要作用是模拟地表,其构成是N行M列的顶点形成的N*M个长方行网格,我们暂时定义每个小长方形网格为整个地形的一个地形块.
高度图则保存了网格中么个顶点的高度信息,用于模拟高山盆地等高度变化
三. 地形基本实现方式及代码:
实现步骤:
1、初始化地形网格或者地形顶点缓冲及地形纹理
2、 加载高度图获取地形高度信息或者自定义高度信息
3、 绘制地形
具体相关代码:
a. 初始化地形网格数据(此处是已顶点缓冲形式创建当然也可以直接加载MESH更加方便)
注:此处仅仅是初始化地形网格数据根据地形长宽创建地形的顶点缓冲数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | struct TerrainVertices { VECTOR3 vPos; //VECTOR3 vNoraml; VECTOR2 vTex; }; #define D3DFVF_TERRAINVERTEX (D3DFVF_XYZ /*| D3DFVF_NORMAL*/| D3DFVF_TEX1) HRESULT hr = pDevice->CreateVertexBuffer( nVertexCount * sizeof (TerrainVertices), D3DUSAGE_WRITEONLY, D3DFVF_TERRAINVERTEX, D3DPOOL_MANAGED, &m_pVertexBuf, NULL ); if ( FAILED( hr ) ) { return false ; } TerrainVertices* pVertices; if ( FAILED( m_pVertexBuf->Lock( 0, 0, ( void **)&pVertices, 0 ) ) ) { Release(); return false ; } int fLongCellSpacing = ( int )m_fLongth / ( m_nLongCount - 1 ); int fWidthCellSpacing = ( int )m_fWidth / ( m_nWidthCount - 1 ); int startX = ( int )- m_fLongth / 2; int startZ = ( int )m_fWidth / 2; // coordinates to end generating vertices at int endX = ( int )m_fLongth / 2; int endZ = ( int )- m_fWidth / 2; float uCoordIncrementSize = 1.0f / ( float )m_nLongCount; float vCoordIncrementSize = 1.0f / ( float )m_nWidthCount; //初始化网格顶点数据 int i = 0; for ( int z = startZ; z >= endZ; z -= fWidthCellSpacing) { int j = 0; for ( int x = startX; x <= endX; x += fLongCellSpacing) { // compute the correct index into the vertex buffer and heightmap // based on where we are in the nested loop. int index = i * m_nLongCount + j; float y = GetHeight( float (x), float (z) ); pVertices[index].vPos = VECTOR3( ( float )x, y, ( float )z ); pVertices[index].vTex = VECTOR2( ( float )j * uCoordIncrementSize, ( float )i * vCoordIncrementSize ); j++; // next column } i++; // next row } m_pVertexBuf->Unlock(); |
b. 加载高度图或者计算高度信息也就是GetHeight函数(代码提供了两种方式)
注: 此处才是地形高度图的主要之所在,GetHeight函数中未被注释部分是自己YY计算地形高度就表现而言没有问题,但用的采取更多的是用高度图标识地形高度,(即被注释掉部分代码)。其基本原理是加载一个高度图纹理,并且读取出高度图中颜色信息数据,根据这些数据对应到地形中某点的高度。例如高度图中第一行第一列颜色值则对应地形网格中第一行第一列顶点高度值。为此对于地形上任意一点的高度可以通过其最近顶点高度线性比例求得。(例如:要求(x,z)点高度 应先求得(x,z)在地形中哪个地形块,根据地形快四个顶点高度及(x,z)位置用线性插值的方式可以求出(x,z)点高度信息)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | // 根据点X Z计算高度 float Terrain::GetHeight( float fX, float fZ ) { float y = 0.0f; y += 7.0f * cosf( 0.051f*fX + 0.0f ) * sinf( 0.055f*fX + 0.0f ); y += 7.0f * cosf( 0.053f*fZ + 0.0f ) * sinf( 0.057f*fZ + 0.0f ); y += 1.0f * cosf( 0.101f*fX + 0.0f ) * sinf( 0.105f*fX + 0.0f ); y += 1.0f * cosf( 0.103f*fZ + 0.0f ) * sinf( 0.107f*fZ + 0.0f ); y += 1.0f * cosf( 0.251f*fX + 0.0f ) * sinf( 0.255f*fX + 0.0f ); y += 1.0f * cosf( 0.253f*fZ + 0.0f ) * sinf( 0.257f*fZ + 0.0f ); return y; /** // 若没有高度图则默认高度为0 if ( 0 == m_pHeightTex ) { return 0.0f; } float fLongthStart = - m_fLongth / 2.0f * m_nLongCount; float fWidthStart = m_fWidth / 2.0f; // 定位改坐标点所在地图中网格位置 float fLongth = m_nLongCount * ( fX - fWidthStart ) / m_fLongth; float fWidth = m_nWidthCount * ( 1 - ( fZ + fWidthStart ) / m_fWidth ); // 判断当前所在点受上半部分还是下半部分三角形影响 UINT nWidth = (int)fWidth; UINT nLongth = (int)fLongth; if ( nLongth < 0 || nLongth >= m_nHeighLongth || nWidth < 0 || nWidth >= m_nHeighWidth ) { return 0.0f; } // 定义三角形三个顶点高度 float fFirst = 0.0f; float fScened = 0.0f; float fThird = 0.0f; if ( fWidth - float(nWidth) > 0.5 ) { fFirst = m_fHeighList[ nWidth*m_nHeighLongth + nLongth ]; fScened = m_fHeighList[ nWidth*m_nHeighLongth + nLongth + 1 ]; fThird = m_fHeighList[ (nWidth + 1)*m_nHeighLongth + nLongth + 1 ]; } else { fFirst = m_fHeighList[ nWidth*m_nHeighLongth + nLongth ]; fScened = m_fHeighList[ (nWidth + 1)*m_nHeighLongth + nLongth + 1 ]; fThird = m_fHeighList[ nWidth*m_nHeighLongth + nLongth + 1 ]; } float fHeight = 0.0f; float fLongPer = fLongth - nLongth; float fWidthPer = fWidth - nWidth; fHeight = ( fFirst * fLongPer + fScened * ( 1 - fLongPer ) + fFirst * fWidthPer + fThird * ( 1- fWidthPer ) ) / 2.0f; return fHeight; */ } |
c. 绘制的地形
当然绘制的方式很简单即绘制顶点缓冲区中顶点(如果用MESH做则直接绘制MESH)在此不再赘述。
四. 效果展示:
模拟大海
五.其他:
地形不只单单包含山地盆地 配合合理的贴图 再加上UV动画或者顶点动画技术也可以模拟海洋等。