![专题 地形绘制基础_第1页](http://file4.renrendoc.com/view/df4b850cbde71145293af2edb4074889/df4b850cbde71145293af2edb40748891.gif)
![专题 地形绘制基础_第2页](http://file4.renrendoc.com/view/df4b850cbde71145293af2edb4074889/df4b850cbde71145293af2edb40748892.gif)
![专题 地形绘制基础_第3页](http://file4.renrendoc.com/view/df4b850cbde71145293af2edb4074889/df4b850cbde71145293af2edb40748893.gif)
![专题 地形绘制基础_第4页](http://file4.renrendoc.com/view/df4b850cbde71145293af2edb4074889/df4b850cbde71145293af2edb40748894.gif)
![专题 地形绘制基础_第5页](http://file4.renrendoc.com/view/df4b850cbde71145293af2edb4074889/df4b850cbde71145293af2edb40748895.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
专题地形绘制基础第一页,共四十页,2022年,8月28日抛砖引玉我们通常将游戏场面架设在起伏的山坡、绿草、沙滩、雪地、树木、阳光和雾等烘托出的自然环境中,这样的场景可增加游戏的逼真度和吸引力。大家玩过的游戏中是否有非常逼真的场景给你留下了深刻的印象的?Copyright2008ByNeusoftGroup.Allrightsreserved第二页,共四十页,2022年,8月28日麻雀虽小,五脏俱全地形网格就是一系列三角形栅格(grid),但是方格中的每个顶点都被赋予了一个高度值(高度或海拔),这样该方格就可通过方格对应高度的平滑过渡来模拟自然地形中山脉到山谷的变化。然后,通过纹理来表现沙滩、绿草如茵丘陵和雪山等。Copyright2008ByNeusoftGroup.Allrightsreserved三角形栅格高度平滑过渡的三角形栅格具有光照和纹理的地形截图第三页,共四十页,2022年,8月28日按部就班我们这个专题的主要内容有:地形高度图的创建和修改创建地形的几何信息对地形进行纹理映射和光照处理实现摄象机在地形中行走的效果Copyright2008ByNeusoftGroup.Allrightsreserved第四页,共四十页,2022年,8月28日地形高度图从几何形态来看,不同的地形外貌,取决于地形表面的每一个点距离地平面的高度值。这些高度值可用二维数组来存储,数组中的每一个元素就与地平面上每一个顶点的高度值一一对应。Copyright2008ByNeusoftGroup.Allrightsreserved地平面顶点地形网格顶点高度值第五页,共四十页,2022年,8月28日我们使用高度图(heightmap)来描述地形中的丘陵和山谷。通常高度图以灰度图(grayscalemap)的形式保存起来。灰度图中的亮度越大,表示地形中某一点的海拔越高。Copyright2008ByNeusoftGroup.Allrightsreserved第六页,共四十页,2022年,8月28日创建高度图生成一个地形高度数据的最直接的方法是,利用图形编辑软件(如photoshop),绘制一个与地平面相同尺寸的灰度图形,用作地形高度图。图形绘制完毕后,将其保存为8位的RAW(原始二进制文件)文件,RAW文件连续存储了图像中以字节为单位的每个像素的灰度值。高度图被保存在磁盘中,通常为其每个元素只分配一个字节的存储空间,这样高度只能在区间[0,255]内。若0~255的高度值不足以表达我们要表达的地形,可以将高度图数据加载到程序中时,对其进行一个比例变换,重新分配一个整型或浮点型数组来存储这些高度值。Copyright2008ByNeusoftGroup.Allrightsreserved第七页,共四十页,2022年,8月28日我们在地形类(Terrain)中定义一个成员函数(readRawFile)用于读取高度图中的高度信息到一个向量里面:代码中的变量_heightmap是Terrain类的一个成员变量。Copyright2008ByNeusoftGroup.Allrightsreservedstd::vector<int>_heightmap;boolTerrain::readRawFile(std::stringfileName){//Aheightforeachvertexstd::vector<BYTE>in(numVertices);std::ifstreaminFile(fileName.c_str(),std::ios_base::binary);if(inFile==0)returnfalse;inFile.read((char*)&in[0],//bufferin.size());//numberofbytestoreadintobufferinFile.close();
//copyBYTEvectortointvector_heightmap.resize(_numVertices);for(inti=0;i<in.size();i++)_heightmap[i]=in[i];returntrue;}第八页,共四十页,2022年,8月28日访问和修改高度图Terrain类提供以下2个函数访问和修改高度图中的指定项:代码中的变量numVertsPerRow是Terrain类的一个成员变量。这两个方法允许我们通过行和列索引引用高度图中指定的项。Copyright2008ByNeusoftGroup.AllrightsreservedintTerrain::getHeightmapEntry(introw,intcol){return_heightmap[row*_numVertsPerRow+col];}voidTerrain::setHeightmapEntry(introw,intcol,intvalue){_heightmap[row*_numVertsPerRow+col]=value;}第九页,共四十页,2022年,8月28日创建地形几何信息Copyright2008ByNeusoftGroup.Allrightsreserved右图向我们展示了地形的一些相关的属性和术语。我们通过指定每行和每列的顶点数以及单元间距(cellspacing)来定义地形的尺寸。这些值将作为Terrian类的构造函数的输入参数。Terrian的构造函数还传入了与地形相关的设备指针、标识存储高度图数据的文件的字符串,以及一个用于对高度图中各元素实施比例变换的高度比例因子(heightScale)。三角形栅格的属性,栅格上的点代表顶点第十页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.AllrightsreservedclassTerrain{public:Terrain(IDirect3DDevice9*device,std::stringheightmapFileName,intnumVertsPerRow,intnumVertsPerCol,intcellSpacing,//spacebetweencellsfloatheightScale);//valuetoscaleheightsby
...methodssnippedprivate:
...device/vertexbufferetcsnippedint_numVertsPerRow;int_numVertsPerCol;int_cellSpacing;int_numCellsPerRow;int_numCellsPerCol;int_width;int_depth;int_numVertices;int_numTriangles;float_heightScale;};第十一页,共四十页,2022年,8月28日我们可由构造函数的传入参数计算出地形的其他变量:Copyright2008ByNeusoftGroup.Allrightsreserved_numCellsPerRow=_numVertsPerRow-1;_numCellsPerCol=_numVertsPerCol-1;_width=_numCellsPerRow*_cellSpacing;_depth=_numCellsPerCol*_cellSpacing;_numVertices=_numVertsPerRow*_numVertsPerCol;_numTriangles=_numCellsPerRow*_numCellsPerCol*2;第十二页,共四十页,2022年,8月28日地形的顶点结构定义如下:Copyright2008ByNeusoftGroup.AllrightsreservedstructTerrainVertex{TerrainVertex(){}TerrainVertex(floatx,floaty,floatz,floatu,floatv){_x=x;_y=y;_z=z;_u=u;_v=v;}float_x,_y,_z;float_u,_v;staticconstDWORDFVF;};ConstDWORDTerrain::TerrainVertex::FVF=3DFVF_XYZ|D3DFVF_TEX1;注意:TerrainVertex是一个嵌套类(nestedclass),该类只在Terrian类的内部使用。第十三页,共四十页,2022年,8月28日1、顶点的计算为了计算三角形栅格的各顶点,我们只需自顶点start起,逐行生成每个顶点,保持相邻顶点的行列间隔均为单元间距(cellspacing),直至到达顶点end为止。这样可以得到每个顶点的x和z坐标的定义,而每个顶点的y坐标的值我们要根据所加载的高度图数据结构中的相应项来获得。Copyright2008ByNeusoftGroup.Allrightsreserved第十四页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved计算纹理坐标:结合下图来看,我们可以推导出地形中位于(i,j)的顶点相对应的纹理坐标(u,v)的计算公式。地形中的顶点与纹理顶点的对应关系u=j*uCoordIncrementSizev=i*vCoordIncrementSize其中:第十五页,共四十页,2022年,8月28日下面是生成各顶点的代码:Copyright2008ByNeusoftGroup.AllrightsreservedboolTerrain::computeVertices(){HRESULThr=0;hr=_device->CreateVertexBuffer(_numVertices*sizeof(TerrainVertex),D3DUSAGE_WRITEONLY,TerrainVertex::FVF,D3DPOOL_MANAGED,&_vb,0);if(FAILED(hr))returnfalse;
//对应第一个生成的顶点坐标intstartX=-_width/2;intstartZ=_depth/2;
//对应最后一个生成的顶点坐标intendX=_width/2;intendZ=-_depth/2;//计算相邻纹理坐标的增量值
floatuCoordIncrementSize=1.0f/(float)_numCellsPerRow;floatvCoordIncrementSize=1.0f/(float)_numCellsPerCol;第十六页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.AllrightsreservedTerrainVertex*v=0;_vb->Lock(0,0,(void**)&v,0);inti=0;
for(intz=startZ;z>=endZ;z-=_cellSpacing){intj=0;for(intx=startX;x<=endX;x+=_cellSpacing){
//计算正确的顶点信息放入顶点缓存中intindex=i*_numVertsPerRow+j;v[index]=TerrainVertex((float)x,(float)_heightmap[index],(float)z,(float)j*uCoordIncrementSize,(float)i*vCoordIncrementSize);j++;//下一列}i++;//下一行
}_vb->Unlock();returntrue;}第十七页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved2、索引的计算为计算三角形栅格各顶点的索引,我们只需自右图的左上角起至右下角,依次遍历每个方格,并计算构成每个方格的三角面片的顶点索引。由右图我们可以推导出一个用于求出构成第i行、第j列的方格的两个面片的顶点索引的通用公式:∆ABC={i·numVertsPerRow+ji·numVertsPerRow+j+1(i+1).numVertsPerRow+j}∆CBD={(i+1)numVertsPerRow+ji·numVertsPerRow+j+1(i+l)numVertsPerRow+j+1}第十八页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved下面是计算索引的代码:boolTerrain::computeIndices(){HRESULThr=0;hr=_device->CreateIndexBuffer(_numTriangles*3*sizeof(WORD),//每个三角形有3个索引D3DUSAGE_WRITEONLY,D3DFMT_INDEX16,D3DPOOL_MANAGED,&_ib,0);if(FAILED(hr))returnfalse;WORD*indices=0;_ib->Lock(0,0,(void**)&indices,0);
第十九页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved//将组成一个方格的2个三角形的一组6个索引的开始位置编入索引intbaseIndex=0;
//从头到尾计算每一个格子中的三角形for(inti=0;i<_numCellsPerCol;i++)//行循环{for(intj=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;
//nextquadbaseIndex+=6;}}_ib->Unlock();returntrue;}第二十页,共四十页,2022年,8月28日纹理映射Terrain类的纹理映射方式是加载一个已经创建好的纹理文件,然后再应用该纹理数据。右边的代码就是将纹理数据自文件加载到了_tex数据成员中,其中_tex是一个指向IDirect3DTexture9接口的指针。Copyright2008ByNeusoftGroup.AllrightsreservedboolTerrain::loadTexture(std::stringfileName){HRESULThr=0;hr=D3DXCreateTextureFromFile(_device,fileName.c_str(),&_tex);if(FAILED(hr))returnfalse;returntrue;}第二十一页,共四十页,2022年,8月28日光照为了给地形场景增强真实感,我们在场景中添加光照的成员函数Terrain::lightTerrain。由于地形是静态的,且光源一般也不发生移动,所以我们可以预先对光照进行计算,这样节省了Direct3D实时照亮地形那部分计算时间。并且这样的计算无需给每个顶点存储顶点的法向量,所以可以节省大量内存Copyright2008ByNeusoftGroup.Allrightsreserved第二十二页,共四十页,2022年,8月28日我们计算地形的明暗用到最基本的漫反射光的光照。给定一个平行光源,用“到达光源的方向”(该光源发出的平行光的传播方向的反方向)来描述该平行光源。再计算地形中的每个坐标方格计算光向量L和该方格的面法向量N之间的夹角。由右图可知,上述夹角越大,坐标方格的朝向偏离光源就越大,其所接收到的光照就越少。反之,越多。当夹角超过90度,方格表明接收不到任何光照。第二十三页,共四十页,2022年,8月28日由光向量和方格的面法向量之间的角度关系,我们可以构造一个位于区间[0,1]内的明暗因子(shadingscalar),来表示方格表明所接收到的光照的量。明暗因子接近于0,则表示这两个向量的夹角很大。当纹理的颜色与其相乘时,颜色值就趋于0,从而呈现出教暗的视觉效果。反之,若该值接近1,则表示两个向量的夹角很小,当纹理的颜色值与其相乘时,该颜色基本保持了原来的亮度。Copyright2008ByNeusoftGroup.Allrightsreserved第二十四页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved坐标方格的明暗度计算光源的方向L是已知量,我们要想办法求出方格的法向量N。方格中任意两个向量的叉积可得到N,然后再对N进行规范化。右图中我们找到方格中两个非零且不平行的向量:u,v。u=(cellSpacing,by-ay,0)v=(0,cy-ay,-cellSpacing)N=u×v
第二十五页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.AllrightsreservedL和N之间的夹角我们转化为求他们的点积,3D空间中的两个单位向量的点积等于这两个向量之间夹角的余弦。S=L·N-1<s<1当s<0时,取s=0。代码:Terrain::computeShade来计算明暗因子:floatcosine=D3DXVec3Dot(&n,directionToLight);if(cosine<0.0f)cosine=0.0f;floatTerrain::computeShade(intcellRow,intcellCol,D3DXVECTOR3*directionToLight){
//取得方格中三个顶点的高度(从高度图中)floatheightA=getHeightmapEntry(cellRow,cellCol);floatheightB=getHeightmapEntry(cellRow,cellCol+1);floatheightC=getHeightmapEntry(cellRow+1,cellCol);第二十六页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved//创建方格中的二个顶点D3DXVECTOR3u(cellSpacing,heightB-heightA,0.0f);D3DXVECTOR3v(0.0f,heightC-heightA,-cellSpacing);//用方格中的二个向量的叉积找到面法线D3DXVECTOR3n;D3DXVec3Cross(&n,&u,&v);D3DXVec3Normalize(&n,&n);floatcosine=D3DXVec3Dot(&n,directionToLight);if(cosine<0.0f)cosine=0.0f;returncosine;}第二十七页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved对地形进行着色我们已经可以计算出每个方格的明暗因子,现在,只需遍历地形中的每个方格,计算其明暗因子,然后将该方格对应的纹理元的颜色与该因子相乘。下面一段代码展示了Terrain::lightTerrain方法的重要部分。DWORD*imageData=(DWORD*)lockedRect.pBits;for(inti=0;i<textureDesc.Height;i++){for(intj=0;j<textureDesc.Width;j++){intindex=i*lockedRect.Pitch/4+j;
//取得当前单元的颜色值D3DXCOLORc(imageData[index]);
//给当前单元着色c*=computeShade(i,j,lightDirection);;
//保存着色结果imageData[index]=(D3DCOLOR)c;}}第二十八页,共四十页,2022年,8月28日在地形中“行走”地形的绘制以及纹理映射、光照的处理我们都完成了,现在我们来实现移动摄像机模拟在场景中行走的过程。摄像机的实现我们已经在前面的专题中实现了,可以直接使用封装好的摄像机类来使用摄像机。我们要让摄像机在地形中行走,不能穿地形到下面,或者悬浮到地形上面。最主要的就是要根据摄像机的位置来确定它的高度(y坐标)。过程:依据给定的x和z坐标找到我们所处的坐标方格。Terrain::getHeight函数实现了该功能,它以x和z坐标为参数,并返回摄像机应处在的高度(海拔)。Copyright2008ByNeusoftGroup.Allrightsreserved第二十九页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.AllrightsreservedfloatTerrain::getHeight(floatx,floatz){
//在xz平面上把地形的START点移动到坐标原点的位置x=((float)width/2.0f)+x;z=((float)depth/2.0f)-z;
//把cellspacing进行比例变换让其等于1x/=(float)cellSpacing;z/=(float)_cellSpacing;首先,进行平移变换,将顶点start平移至坐标原点。然后,通过缩放因子为单元间隔的负倒数的比例变换将坐标方格的单元间隔归一化。现在,我们就转换到了一个新的参考系中,其中z轴方向向“下”(理解上)。上述过程如右图所示第三十页,共四十页,2022年,8月28日Copyright2008ByNeusoftGroup.Allrightsreserved经过这样的变换以后,坐标系与矩阵的顺序保持了一致。即左上角为原点,列索引和行索引分别沿着向右方向和向下方向递增。目前单元间距为1,所以,我们可以迅速求出当前我们所处的坐标方格的行列索引。知道了坐标方格的行列索引,我们就知道了当前所处的坐标方格,就可求出构成该方格的4个顶点的高度。floatcol=::floorf(x);//小于等于x的最大整数值floatrow=::floorf(z);//AB//*--* //|/|
//*--* //CD
floatA=getHeightmapEntry(row,col);floatB=getHeightmapEntry(row,col+1);floatC=getHeightmapEntry(row+1,col);floatD=getHeightmapEntry(row+1,col+1);第三十一页,共四十页,2022年,8月28日如右图所示,当前所处的方格位置以及构成该方格的4个顶点的高度均已知。我们要求摄像机位于任意的位置(x,z)时,坐标方格单元的高度。因为方格可能朝多个方向发生倾斜,所以我们要求出这个y值相对比较困难。第三十二页,共四十页,2022年,8月28日为了求出摄像机所在位置的高度,我们首先需要判断摄像机当前处于哪个坐标方格内。而每个坐标方格都是由两个三角面片的组合来绘制的。为了求出摄像机当前所处的三角形面片,我们需要对当前所处的坐标方格进行变换,使其左上角的顶点与坐标原点重合。由于col和row描述了当前坐标方格的左上角顶点的位置,我们必须沿x轴平移-col个单位,并沿z轴平移-row个单位。floatdx=x-col;floatdz=z-row;第三十三页,共四十页,2022年,8月28日如果dz<1.0-dx,我们当前就位于上三角形面片△v0v1v2中,否则就在下三角形面片△v0v2v3中。当摄像机处于上三角面片中时,我们沿着该三角形的AB、AC两个边,以向量q=(qx,A,qz)为始端分别构造两个向量u=(cellSpacing,B-A,0),v=(0,C-A,-cellSpacing)。我们沿着u轴对点dx进行线性插值,再沿着v轴对dz点进行线性插值。若给定x坐
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度国际贸易实务实训报告标准合同范本
- 沪教版(上海)七年级地理第一学中国区域篇(上)2《自主学习 认识区域-浙江省》听课评课记录
- 2025年度环保设备购置及污染赔偿合同
- 2025年度窗帘品牌跨界合作与营销推广合同
- 2025年度国际邮政包裹运输服务合同规定
- 智研咨询发布:2025年中国玄武岩纤维行业市场发展环境及前景研究报告
- 2025年中国电子取证行业深度分析、投资前景、趋势预测报告(智研咨询)
- 2025年度绿色核桃产地直供采购合同范本
- 给宝宝的表扬信(15篇)
- 2025年度股权激励实施居间服务合同
- 急性胸痛患者的急救护理
- 小红书种草营销师(初级)认证考试真题试题库(含答案)
- 癫痫病人的护理(课件)
- 企业资产管理培训
- 2024年WPS计算机二级考试题库350题(含答案)
- 自然辩证法学习通超星期末考试答案章节答案2024年
- 2024年4月27日浙江省事业单位招聘《职业能力倾向测验》试题
- 2024年6月浙江省高考地理试卷真题(含答案逐题解析)
- 物业管理服务应急响应方案
- 医院培训课件:《如何撰写护理科研标书》
- 风车的原理小班课件
评论
0/150
提交评论