龙书笔记(13)
2014-04-22 09:50
141 查看
chap 13 地形绘制基础
主要是创建一个 地形类 (Terrain)
1.高度图
其实是一个数组,每个元素都指定了地形方格中某一顶点的高度值,每个元素只分配了1个字节的存储空间,
当加载到程序时,重新分配 浮点型 或 整型 数据来存储这些高度值,高度图的图形表示有很多,
比如:灰度图,地形中某点海拔越高,相应点在灰度图中的亮度越大
(1)创建高度图
用photoshop完成是不错的选择,一旦完成高度图的创建,将其保存为 8位 的RAW文件
RAW文件:仅连续存储图像中以字节为单位的每个像素的灰度值,他的特点是读取方便,可以理解为一个连续的字节存储块
当photoshop提示是否为RAW文件增加一个文件头时,选择"否"
(2)加载RAW文件
(3)访问和修改高度值
2.创建地形集合信息
每行顶点数 和 单元数,每列顶点数 和 单元数,高度比例因子,总宽度,总深度,总顶点数,三角元数
地形顶点(TerrainVertex) 是 一个嵌套类,他只在地形类(Terrain)中有用
(1)顶点计算
(2)索引计算
3.纹理映射
方案一:
方案二:
还有另一种纹理映射的方式,他的主要思路是使用一个"空"的纹理,然后基于一些已定义好的参数计算出纹理元的颜色,
例如,可以根据顶点不同的高度给出不同的颜色
(1)D3DXCreateTexture 创建一个空纹理
(2)由于一个纹理对象可有多级渐近纹理,所以要先 锁定 顶层纹理
(3)遍历每个纹理元并对其上色
//第一种方法是 通过文件去创建纹理,可以理解为直接粘贴纹理图
//第二种方法是 创建空纹理,然后再依据某些特定属性去生成的"立即纹理"
4.光照
方案一:
自己设置灯
方案二:
关键:计算地形三角元的明暗因子,他是一个0到1之间的浮点数
使用指定 到达光源的方向 来描述平行光 的光向量(L) 是为了更适合 漫射光光照的 计算
每个方格的面法向量设为N
注意:光的方向向量应为单位向量,这是为了求后面的cosine值,因为面的法向量也将被单位化,在对LN进行 点乘 的时候,直接得到明暗因子
LN夹角越大,此方格接受到的光照就越少,当这个角度超过了90度,很明显,光就照不到方格了
5.在地形中“行走”
关键点:得到对应x,z坐标的高度值
主要是创建一个 地形类 (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; //求得坐标所在点的高度值 }
相关文章推荐
- Javascript 笔记与总结(2-13)定时器 setTimeout 和 setInterval
- 【深度学习】笔记13 win10+cuda7.5+caffe+vs2013环境搭建(GPU版本)
- stl 学习笔记13 advance()distance() iter_swap()
- 【Android基础笔记13】SQLiteDatabase
- 复习笔记13 函数 重载 内联
- HTML5+CSS3+JS学习笔记-13-CSS3过渡和CSS3动画
- Spark2.x学习笔记:13、Spark SQL快速入门
- 每天一个 linux 命令(13):less 命令---学习笔记
- Bootstrap基本组件学习笔记之缩略图(13)
- 嵌入式学习笔记(13)——51单片机之D/A转换器
- CSS自学笔记(13):CSS3 2D/3D转换
- C++学习笔记13,private继承,私有继承(四)
- C语言学习笔记(13指针)
- (13)Java笔记之JDK7特性简述
- C++学习笔记13-类继承
- 《算法导论》笔记(13) 单源最短路径,所有结点对的最短路径
- YII框架分析笔记13:单元测试
- 龙书笔记(9)
- 台湾大学林轩田机器学习基石课程学习笔记13 -- Hazard of Overfitting
- X Window研究笔记(13)