您的位置:首页 > 其它

龙书笔记(13)

2014-04-22 09:50 141 查看
chap 13 地形绘制基础
主要是创建一个 地形类 (Terrain)

1.高度图
其实是一个数组,每个元素都指定了地形方格中某一顶点的高度值,每个元素只分配了1个字节的存储空间,
当加载到程序时,重新分配 浮点型 或 整型 数据来存储这些高度值,高度图的图形表示有很多,
比如:灰度图,地形中某点海拔越高,相应点在灰度图中的亮度越大

(1)创建高度图
用photoshop完成是不错的选择,一旦完成高度图的创建,将其保存为 8位 的RAW文件
RAW文件:仅连续存储图像中以字节为单位的每个像素的灰度值,他的特点是读取方便,可以理解为一个连续的字节存储块
当photoshop提示是否为RAW文件增加一个文件头时,选择"否"

(2)加载RAW文件
std::vector<int> _heightmap;				//分配整型数据来存储高度值
bool Terrain::readRawFile(std::string fileName)
{
std::vector<BYTE> in(_numVertices);
std::ifstream inFile(fileName.c_str(), std::ios_base::binary);
if(inFile == 0)
return false;
inFile.read( (char *)&in[0], in.size() );	//把RAW文件中的内容读入到BYTE容器in里面
inFile.close();
_heightmap.resize( _numVertices );
for(int i = 0; i<in.size(); i++)
_heightmap[i] = in[i];			//再把in的内容赋给int容器_heightmap,这样就得到了每个顶点的整型 高度值
return true;
}


(3)访问和修改高度值
int  Terrain::getHeightmapEntry(int row, int col)
{
return _heightmap[row * _numVertsPerRow + col];
}
void Terrain::setHeightmapEntry(int row, int col, int value)
{
_heightmap[row * _numVertsPerRow + col] = value;
}


2.创建地形集合信息
每行顶点数 和 单元数,每列顶点数 和 单元数,高度比例因子,总宽度,总深度,总顶点数,三角元数
地形顶点(TerrainVertex) 是 一个嵌套类,他只在地形类(Terrain)中有用

(1)顶点计算
bool Terrain::computeVertices()
{
HRESULT hr = 0;
hr = _device->CreateVertexBuffer(
_numVertices * sizeof(TerrainVertex);
D3DUSAGE_WRITEONLY,
TerrainVertex::FVF,
D3DPOOL_MANAGED,
&_vb,
0
);
if(FAILED(hr))
return false;
int startX = -_width/2;
int startZ =  _depth/2;
int endX =  _width/2;
int endZ = -_depth/2;
float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;
float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;

TerrainVertex *v = 0;
_vb->Lock(0,0,(void**)&v,0);
int i = 0;
for(int z = startZ; z >= endZ; z -= _cellSpacing)
{
int j = 0;
for(int x = startX; x <= endX; x += cellSpacing)
{
int index = i * _numVertsPerRow + j;
v[index] = Terrain(
(float)x,
(float)_heightmap[index],
(float)z,
(float)j*uCoordIncrementSize,
(float)i*vCoordIncrementSize
);
j++;
}
i++;
}
_vb->Unlock();
return true;
}


(2)索引计算
//为什么要用索引,因为创建的顶点是按行创建的,不是按三角元创建的
bool Terrain::computeIndices()
{
HRESULT hr = 0;
hr = _device->CreateIndexBuffer(
_numTriangles * 3 * sizeof(WORD),
D3DUSAGE_WRITEONLT,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&_ib,
0
);
if(FAILED(hr))
return false;
WORD *indices = 0;
_ib->Lock(0,0,(void**)&indices,0);
int baseIndex = 0;
for(int i=0; i<_numCellsPerCol; i++ )
{
for(int j=0; j<_numCellsPerRow; j++)
{
indices[baseIndex] 		= i * _numVertsPerRow + j;
indices[baseIndex + 1] 	= i * _numVertsPerRow + j + 1;
indices[baseIndex + 2]	= (i+1) * _numVertsPerRow + j;
indices[baseIndex + 3]	= (i+1) * _numVertsPerRow + j;
indices[baseIndex + 4]	= i * _numVertsPerRow +j + 1;
indices[baseIndex + 5]	= (i+1) * _numVertsPerRow + j +1;
baseIndex += 6;
}
}
_ib->Unlock();
return true;
}


3.纹理映射

方案一:
bool Terrain::LoadTexture(std::string fileName)
{
HRESULT hr = 0;
hr = D3DXCreateTextureFromFile(
_device,
fileName.c_str(),
&_tex
);
if(FAILED(hr))
return false;
return true;
}

方案二:
还有另一种纹理映射的方式,他的主要思路是使用一个"空"的纹理,然后基于一些已定义好的参数计算出纹理元的颜色,
例如,可以根据顶点不同的高度给出不同的颜色
(1)D3DXCreateTexture 创建一个空纹理
(2)由于一个纹理对象可有多级渐近纹理,所以要先 锁定 顶层纹理
(3)遍历每个纹理元并对其上色
//第一种方法是 通过文件去创建纹理,可以理解为直接粘贴纹理图
//第二种方法是 创建空纹理,然后再依据某些特定属性去生成的"立即纹理"
bool Terrain::genTexture(D3DXVECTOR3 * directionToLight)
{
HRESULT hr = 0;
int texWidth  = _numCellsPerRow;
int texHeight = _numCellsPerCol;

hr = D3DXCreateTexture(
_device,
texWidth, texHeight,
0,			//创建一个完整的多级渐近纹理链
0,			//Usage
D3DFMT_X8R8G8B8,
D3DPOOL_MANAGED,
&_tex
);
if(FAILED(hr))
return false;
//确保顶点的格式
D3DSURFACE_DESC textureDesc;
_tex -> GetLevelDesc(0, &textureDesc);
if(textureDesc.Format != D3DFMT_X8R8G8B8)
return false;

D3DLOCKED_RECT lockedRect;
_tex -> LockRect(
0,
&lockedRect,
0,
0
);
DWORD *imageData = (DWORD*)lockedRect.pBits;
//pitch每行字节数,pBits第一个字节地址
for(int i = 0; i < texHeight; i++)
{
for(int j = 0; j < texWidth; j++)
{
D3DXCOLOR c;
float height = (float)getHeightmapEntry(i,j);

if((height)<42.5f)				c = d3d::BEACH_SAND;
else if((height)<85.0f)			c = d3d::LIGHT_YELLOW_GREEN;
else if((height)<127.5f)		c = d3d::PUREGREEN;
else if((height)<170.0f)		c = d3d::DARK_YELLOW_GREEN;
else if((height)<212.5f)		c = d3d::DARKBROWN;
else 							c = d3d::WHITE;

imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;
}
}
_tex->UnlockRect(0);
if(!lightTerrain(directionToLight))				//照亮地形,主要是明暗因子的计算
{
::MessageBox(0,"lightTerrain() - FAILED",0,0);
return false;
}
hr = D3DXFilterTexture(
_tex,
0,
0,
D3DX_DEFAULT
);
if(FAILED(hr))
return false;
return true;
}


4.光照

方案一:
自己设置灯

方案二:
关键:计算地形三角元的明暗因子,他是一个0到1之间的浮点数

使用指定 到达光源的方向 来描述平行光 的光向量(L) 是为了更适合 漫射光光照的 计算
每个方格的面法向量设为N
注意:光的方向向量应为单位向量,这是为了求后面的cosine值,因为面的法向量也将被单位化,在对LN进行 点乘 的时候,直接得到明暗因子

LN夹角越大,此方格接受到的光照就越少,当这个角度超过了90度,很明显,光就照不到方格了
//计算每个面的明暗因子
float Terrain::ComputeShade(int cellRow, int cellCol, D3DXVECTOR3 *directionToLight)
{
float heightA = getHeightEntry(cellRow, cellCol);
float heightB = getHeightEntry(cellRow, cellCol + 1);
float heightC = getHeightEntry(cellRow + 1, cellRow);

D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);
D3DXVECTOR3 v(0.0f, heightC - heightA, -_cellSpacing);

D3DXVECTOR3 n;
D3DXVec3Cross(&n, &u, &v);
D3DXVec3Normalize(&n, &n);

float cosine = D3DXVec3Dot(&n, directionToLight);		//计算出法向量和光照方向的cos值,这是一个0~1之间的数

if(cosine<0.0f)
cosine = 0.0f;
return cosine;
}
//对地形进行着色,也称 明暗处理
DWORD *imageData = (DWORD*)lockedRect.pBits;
for(int i = 0; i<textureDesc.Height; i++)
{
for(int j = 0; j<textureDesc.Width; j++)
{
int index = i * lockedRect.Pitch / 4 + j;
D3DXCOLOR c(imageData[index]);

c *= ComputeShade(i, j, directionToLight );
iamgeData[index] = (D3DCOLOR)c;
}
}


5.在地形中“行走”

关键点:得到对应x,z坐标的高度值
float Terrain::getHeight(float x, float z)
{
x = ((float)_width / 2.0f) + x;
z = ((float)_depth / 2.0f) - z;
x /= (float)_cellSpacing;
z /= (float)_cellSpacing;
float col = ::floorf(x);
float row = ::floorf(z);						//以上可以求出坐标所在的方格
float A = getHeightmapEntry(row,   col);
float B = getHeightmapEntry(row,   col+1);
float C = getHeightmapEntry(row+1, col);
float D = getHeightmapEntry(row+1, col+1);		//得此方格的4个顶点
float dx = x - col;								//把此方格移到起始点
float dz = z - row;
float height = 0.0f;
if(dz < 1.0f - dx) 								//判断此点是位于方格的上三角面还是下三角面
{
float uy = B - A;
float vy = C - A;
height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);
}
else
{
float uy = C - D;
float vy = B - D;
height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);
}
return height;									//求得坐标所在点的高度值
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: