x趋于无穷大的算法,麻烦能跟我简单排序算法讲一下怎么算吗,比如说这一题

<span style="color: #&srand((unsigned)time(<span style="color: #));<span style="color: #&<span style="color: #&mCoarseConstant=pow(<span style="color: #.5,H);<span style="color: #&mSideLength=pow(<span style="color: #,mIndex)+<span style="color: #;
&& ~FractalTerrainGenerator( void )&&& 当FTG对象被销毁时会调用析构函数。像其他析构函数一样,这里它也仅仅负责一件事:安全释放动态申请的内存资源。成员中有两个指针指向了这样的内存块。因此会安全释放它们。
<span style="color: #&SAFE_DELETE(mHeightDataBuffer);<span style="color: #&SAFE_DELETE(mHeightDataFlag);
注意:SAFE_DELETE是一个宏。其定义为:#define SAFE_DELETE(x) { if(x){delete[] (x); (x) = NULL;} }
&&& Generate( const std::string& output_file )&&& 这是公共函数中最重要的方法了。因为缺少了它,你就无法启动这个类工作。该方法对使用该类的客户端开放。参数output_file指定要输出的高度图的文件名。在这个方法内部,依次调用protect域内的函数。调用次序为:InitGenerator(),CoreCalculate(),OutputBufferToFile(output_file)。从中你可以看出它的运行逻辑:首先初始化,然后进行核心计算,最后将得出的结果存放在指定的文件中。
<span style="color: #&InitGenerator();<span style="color: #&<span style="color: #&CoreCalculate();<span style="color: #&<span style="color: #&OutputBufferToFile(output_file);
&&& Getxxx...&&& 以Get开头公共函数都是作为其它类的接口。这里说的其它类,就是指TMG(Texture Mapping Generator)。因为TMG的工作需要FTG来提供,比如我们曾说过的&#8220;找对应的高度值&#8221;。有关TMG的内容放在下一节介绍。&&&&&&&&
Protect成员介绍:
&&& GetRandomValue( void )&&& 取得随机值。该随机值由mRandomRange, mTerrainHeightExtent和一丁点运气决定(要不怎么叫&#8220;随机&#8221;值呢)。
<span style="color: #&short&random&=&((rand()%mRandomRange)&-&(mRandomRange/<span style="color: #)&&)&&*&mTerrainHeightExtent&;<span style="color: #&<span style="color: #&return&
&&&&&&&InitGenerator( void )&&& 该方法执行真正的初始化工作。首先,它将容器指针与对应的容器绑定在一起;其次,申请一个存放高度图数据的空间(高度图缓冲区),配备一个存放&#8220;高度图数据是否被填充&#8221;的旗标空间,并初始化该旗标空间全部元素为假;再次,构建第一个Square,并把该Square插入到Square队列中;最后,将这个Square四个角落值对应的高度图缓冲区位置都填充为mBaseValue。
&1&&&&&//<span style="color: #.将容器指针绑定&2&&&&&mSquarelistPointer=&mS&3&&&&&mSquarelist_backPointer=&mSquarelist_&4&&5&&&&&//<span style="color: #.申请一个存放高度图数据的空间,配备一个存放&#8220;高度图数据是否被填充&#8221;的旗标空间,并初始化该旗标空间全部元素为假&6&&&&&uint&capacity=mSideLength*mSideL&7&&&&&mHeightDataBuffer=new&short[capacity];&8&&&&&mHeightDataFlag&&=new&bool[capacity];&9&&&&&memset(mHeightDataFlag,&false,&capacity*sizeof(bool));<span style="color: #&&&&&memset(mHeightDataBuffer,<span style="color: #,capacity*sizeof(short));<span style="color: #&<span style="color: #&&&&&//<span style="color: #.得到四个边角值,构建一个Square,并将该Square插入到Square队列中<span style="color: #&&&&&Square*&root_square&=&new&Square(<span style="color: #,&pow(<span style="color: #,mIndex),&pow(<span style="color: #,mIndex)*(pow(<span style="color: #,mIndex)+<span style="color: #),&pow(pow(<span style="color: #,mIndex)+<span style="color: #,<span style="color: #)-<span style="color: #);<span style="color: #&&&&&mSquarelist.push_back(root_square);<span style="color: #&<span style="color: #&&&&&//<span style="color: #.将根元素的坐标镶嵌到HeightDataBuffer中<span style="color: #&&&&&mHeightDataBuffer[root_square-&LeftTop]=<span style="color: #&&&&&mHeightDataBuffer[root_square-&RightTop]=<span style="color: #&&&&&mHeightDataBuffer[root_square-&LeftBottom]=<span style="color: #&&&&&mHeightDataBuffer[root_square-&RightBottom]=mBaseV
&&& CoreCalculate( void )&&& 这里执行的是核心的运算。最关键之处在于它定义了一个while循环框架。该循环会运行到mHeightDataBuffer中所有元素都已生成,才会结束。一次循环就是前面介绍的&#8220;一轮&#8221;运算。在循环体中,首先调用for_each来调用每一个容器元素参与&#8220;菱形阶段&#8221;(调用CalculateDiamondPhase方法)和&#8220;正方形阶段&#8221;(调用CalculateSquarePhase方法)(注意涉及到stl中的for_each算法和类内成员函数绑定的技巧,比较麻烦);完毕后,先清空使用中的容器元素,再调用SwapSquareListPointer方法交换两个容器先后位置;最后,缩小随机值取值范围。
&1&while(mIndex&<span style="color: #)&2&&&&&{&3&&&&&&&&&//菱形阶段&&&&&&&&&4&&&&&&&&&std::for_each(mSquarelistPointer-&begin(),mSquarelistPointer-&end(),std::bind1st(std::mem_fun(&FractalTerrainGenerator::CalculateDiamondPhase),this));&5&&6&&7&&&&&&&&&//正方形阶段&8&&&&&&&&&std::for_each(mSquarelistPointer-&begin(),mSquarelistPointer-&end(),std::bind1st(std::mem_fun(&FractalTerrainGenerator::CalculateSquarePhase),this));&9&<span style="color: #&<span style="color: #&&&&&&&&&mSquarelistPointer-&clear();<span style="color: #&<span style="color: #&&&&&&&&&SwapSquareListPointer();<span style="color: #&&&&&&&&&<span style="color: #&&&&&&&&&//缩小随机值取值范围<span style="color: #&&&&&&&&&mRandomRange*=mCoarseC<span style="color: #&<span style="color: #&&&&&&&&&//Index值减小1<span style="color: #&&&&&&&&&mIndex--;&&&&&&&&<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&mSquarelistPointer-&clear();
&&& CalculateDiamondPhase(Square* square )&&& 传说中的&#8220;菱形阶段&#8221;。在这里,对每一个square计算其中心值的高度。并把此值存放到mHeightDataBuffer中对应的元素中。 &&& &&& 代码略。
&&& CalculateSquarePhase( Square* square )&&& 传说中的&#8220;正方形阶段&#8221;。这里分两步:&&& 1.计算square的四个次中心值(也就是菱形的中心值):top_center, bottom_center, left_center,right_center。求出它们的高度值,并存放到mHeightDataBuffer中对应的元素中。这里还要判断这四个值是不是位于最大正方形的四条边上(通过JudgeInSide方法)。如果是的话,那还要计算延伸到对面的点。此外,还要判断这四个值是不是已经求算过了。如果是的话,就要忽略掉。&&& 2. 将新得到的四个正方形压入后备容器中。 &&& 代码略。
&&& JudgeInSide( SideType st, uint coord )&&& 判断给定的coord位置是否位于最大正方形的边上,st具体指出是哪条边:_TOP(上), _BOTTOM(下), _LEFT(左), _RIGHT(右)。不同的边计算方法不同。
&1&switch(st)&2&&&&&{&3&&&&&case&_TOP:&4&&&&&&&&&{&5&&&&&&&&&&&&&return&&(&coord&-&mSideLength&&&<span style="color: #&)&?&true&:&false&;&&6&&&&&&&&&&&&&&7&&&&&&&&&}&8&&9&&&&&case&_BOTTOM:<span style="color: #&&&&&&&&&{<span style="color: #&&&&&&&&&&&&&return&&(&(coord&+&mSideLength)&&&(mSideLength&*&mSideLength&-<span style="color: #&)&)&?&true&:&false&;&<span style="color: #&&&&&&&&&}<span style="color: #&<span style="color: #&&&&&case&_LEFT:<span style="color: #&&&&&&&&&{<span style="color: #&&&&&&&&&&&&&return&&(&coord&%&mSideLength&==&<span style="color: #&)&?&true&:&false&;&<span style="color: #&&&&&&&&&}<span style="color: #&<span style="color: #&&&&&case&_RIGHT:<span style="color: #&&&&&&&&&{<span style="color: #&&&&&&&&&&&&&return&&(&(coord+<span style="color: #)&%&mSideLength&==&<span style="color: #&)&?&true&:&false&;&<span style="color: #&&&&&&&&&}<span style="color: #&<span style="color: #&&&&&default&:&break;<span style="color: #&&&&&&&&&<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&return&false;
&& &&& OutputBufferToFile(const std::string& output_file)&&& 将mHeightDataBuffer缓冲区输出至文件。该文件就是raw高度图。output_file指明了文件名。
&1&&&&&&&&&std::ofstream&&2&&&&&ofs.open(output_file.c_str(),&std::ios_base::binary&|&std::ios_base::out&|&std::ios::trunc);&3&&&&&if(ofs.is_open())&4&&&&&{&5&&&&&&&&&ofs.write((const&char*)mHeightDataBuffer,&mSideLength*mSideLength*sizeof(short));&6&&&&&}&7&&&&&else&8&&&&&{&9&&&&&&&&&std::runtime_error("cann't&open&the&output&file&when&call&OutputBufferToFile().&");<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&ofs.close();
&&& SwapSquareListPointer( void )&&& 交换前后备square容器的位置。其实质就是交换mSquarelistPointer和mSquarelist_backPointer指针的位置。
<span style="color: #&&&&&SquareList*&sl_<span style="color: #&&&&&sl_temp=mSquarelistP<span style="color: #&&&&&mSquarelistPointer=mSquarelist_backP<span style="color: #&&&&&mSquarelist_backPointer=sl_
&&& SetMaxHeightValue( short height )&&& 这是一个简单设置mMaxHeightValue值的方法。将参数height与当前mMaxHeightValue值比较,谁大就保留谁为新的mMaxHeightValue。该方法会在每一次新的高度值产生后即可调用。
<span style="color: #&mMaxHeightValue&=&(mMaxHeightValue&&&height)&?&height&:&mMaxHeightValue&;&&
实践收获:
&&& 1.注意灰度图的最小值为0. 你不应该将一个生成的负值存入高度图中,否则渲染出来会看到一个非常高的&#8220;突刺&#8221;。当生成新的高度值后,应该立刻检查它的符号:一旦为负,则将其归置为0 。 这个问题曾带给我无尽的痛苦,看着那一堆堆突刺满天的地形,程序却丝毫没有崩溃。我竟花了整整2天的时间来查找形成&#8220;突刺&#8221;的原因。惨遭蹂躏的2天啊...
&&& 2.随机值的取值应该调试好。是否能产生一个比较真实的地形,取决于随机值的好坏。
&&& 3.stl中的for_each算法并不能天然绑定类成员函数。因此需要bind1st以及mem_fun来帮忙。我不擅长stl,因此吃了很多苦头。总有一天我会找它们算账的。
&&& 这一节是关于&#8220;地形纹理过程生成算法&#8221;的代码。TextureMappingGenerator(纹理贴图生成器,以下简称TMG)是我创造的另一个类。这个类需要FTG才能工作,因此二者有很强的耦合性(主要是TMG非常依赖FTG,FTG并不需要TMG)。当然可以设计成TMG不需要FTG,取而代之去依赖一张高度图。这可以让FTG和TMG完全分开,彼此间没有任何联系。这样的设计虽然更加灵活,但却要付出额外的代价:那就是重新读取高度图数据到内存中,并筛选出最大高度值。这导致要遍历高度图数据起码两次以上。考虑到我的FTG和TMG本身就是为了协同工作而设计的,目前并无分开使用的打算。因此还是采用了耦合性很强的设计。 如此,高度图缓冲区可以直接利用,非常方便。
&&& 另外,由于TMG需要输入作为&#8220;原料&#8221;的纹理图(不仅是一块。由多块组成。为方便区分,以下称它们为Tile),还需要输出已经生成的纹理图。为了避免分散注意力研究&#8220;输入输出&#8221;这些东东,这一块的内容我直接采用了指导手册中的示例代码。导入了CIMAGE类操作相关事务。另外,CIMAGE还配备一个缓冲区用来存放纹理数据,因此也提供了数据的书写方法(这里的CIMAGE就相当于上一节那个简陋的高度图缓冲区)。可见CIMAGE的功能相当强大。根据CIMAGE的特性,我使用了tga格式作为输入的纹理图格式,而采用bmp格式作为输出的纹理图格式。
再见算法思想:
&&& 算法分两步:首先调用InitGenerator方法进行初始化工作,接着调用Generator方法生成纹理图。在初始化过程中,不仅要读入原料纹理图(通过LoadTilesFromFile方法),申请输出用的纹理图空间,还要为每个Tile分配相应的&#8220;管辖高度&#8221;(通过AssainTilesRange方法)。在生成过程中,要遍历输出用的纹理图,对于其中每一个像素,遍历所有Tile,根据可见度(通过CalculateTilePresent方法)调和其中的色度,最终把值累加到对应的输出像素中,并且保存在缓冲区中。最后,保存该缓冲区至文件即完成全部的工作。
下面是该类的成员列表
TMG成员列表&
private成员介绍:
&&& mTileNums:指示Tile的数量。由InitGenerator方法的形参指定。&&& mTileDataBuffer: 指向Tile数组的指针。数组中的每个类型都是CIMAGE。这里存放的所有TIle都是作为&#8220;原料&#8221;的纹理图。&&& mTextureDataBuffer:指向输出的纹理图的指针。它存放着最后生成的数据。保存到新的纹理图中的数据也来自这里。&&& mFTG:指向FTG的指针。这里即是与FTG耦合的地方。程序中需要FTG获得高度图的最大值和遍历时的高度值。&&& mTIleRangeArray:指向TileRange结构数组的指针。TileRange是一个有四个分量的结构体:{id, lowHeight,optimalHeight,hightHeight}。id表示该TileRange的序号,其余三个值就是前面算法中提到的,把管辖高度划分为两个区间的,三个边界点(见这里&链接&)。TileRange与Tile是对应的。但Tile本身存放的是纹理图信息,而它的管辖高度信息存放在了TileRange中。
public成员介绍:
&&& TextureMappingGenerator( void )&&& TMG的构造函数非常简单,没有任何参数,而且函数体内没有做任何工作。这里构造函数得作用仅仅是初始化了所有的成员变量而已。指针置空,整型置0 。&
&&& ~TextureMappingGenerator( void )&&& TMG的析构函数负责释放由成员变量指向的动态内存资源。别无其他。
&&& InitGenerator( FractalTerrainGenerator* ftg, const std::string& base_name, int tile_nums )&&& 这是TMG的初始化函数。负责初始化工作。与FTG的设计不同,这个函数也是开放给使用TMG的客户端使用的。也就是说,为了能够使用TMG,你必须首先自己调用这个函数完成初始化工作,然后才能调用Generate方法。参数中的ftg是要求提供的FTG,在这里将FTG与TMG绑定;base_name是指Tile的文件名基值。由于Tile不可能只有一个,为了能够顺利读取这些Tile资源,你需要将每个Tile命名成除了最后一个字符之外相同的名字,这个相同的名字就是基值。最后一个字符程序将自动根据数量用数字补其。最后的参数tile_nums表示Tile的数量。举个例子:假如读入的Tile有4块,而提供的基值是&#8220;texture_tile_&#8221;,那么程序真正读取的是文件名依次是&#8220;texture_tile_1.tga&#8221;,&#8220;texture_tile_2.tga&#8221;,&#8220;texture_tile_3.tga&#8221;,&#8220;texture_tile_4.tga&#8221;。
&&& 在InitGenerator方法中,读取了对应的tga纹理资源到Tile中,并且为相应TileRange分配合适的管辖高度。它是如何做到的?我们知道要分配合适的管辖高度,需要知道最大高度。而最大高度,就是从FTG中的GetMaxHeightValue方法得到的。
&1&&&&&mFTG=&2&&3&&&&&mTileNums&=&tile_&4&&5&&&&&mTilesDataBuffer&=&new&CIMAGE[mTileNums]&;&6&&&&&if(!LoadTilesFromFile(base_name,&mTilesDataBuffer))&7&&&&&{&8&&&&&&&&&std::runtime_error("can't&load&tiles&from&file");&9&&&&&}<span style="color: #&<span style="color: #&&&&&mTextureDataBuffer&=&new&CIMAGE;&<span style="color: #&&&&&mTextureDataBuffer-&Create(mTilesDataBuffer[<span style="color: #].GetWidth()&,&mTilesDataBuffer[<span style="color: #].GetHeight(),&<span style="color: #);<span style="color: #&&&&&//mTextureDataBuffer-&Create(mFTG-&GetSideLength()-1&,&mFTG-&GetSideLength()-1,&24);<span style="color: #&<span style="color: #&&&&&mTileRangeArray&=&new&TileRange[mTileNums]&;<span style="color: #&&&&&AssainTilesRange(mFTG-&GetMaxHeightValue(),&mTileNums,&mTileRangeArray);
&& Generate( const std::string& output_file )&&& 该方法启动生成器工作,生成最终纹理,并输出纹理到指定的文件中,文件名为output_file。这是第二个开放给使用TMG的客户端的方法。当你调用InitGenerator后,就应该立刻调用这个函数。
&&& 这个方法是TMG运算的核心。它遍历每一个纹理像素,读取高度图缓冲区中对应的高度值,混合每个Tile的可见度,设置最终纹理的颜色值,最后保存纹理到文件中。&&&& 代码略。
Protect成员介绍:
&&& LoadTilesFromFile( std::string base_name, CIMAGE* tiles )&&& 读取&#8220;原料&#8221;纹理图片文件到Tile的方法。参数base_name是文件名基值,tiles表示Tile数量。base_name由InitGenerator函数提供,tiles由成员变量mTilesDataBuffer提供。
&&& 该方法是一个for循环的框架。在循环体内部,会依次为每一个Tile读取相应的文件。注意Tile的类型就是一个CIMAGE,因此读取的方法由它的成员方法LoadData指定。
&1&for(int&i=<span style="color: #;&i&mTileN&i++)&2&&&&&{&3&&&&&&&&&std::string&str&=&base_name&+&int2string(i+<span style="color: #)&+&".tga";&4&&&&&&&&&if(!tiles[i].LoadData((char*)str.c_str()))&5&&&&&&&&&{&6&&&&&&&&&&&&&return&false;&7&&&&&&&&&}&8&&&&&}&9&<span style="color: #&&&&&return&true;
&&&&&&&AssainTilesRange( short max_height, int tile_nums, TileRange* tr_array )&&& 负责为每个Tile分配&#8220;管辖高度&#8221;,并保存在对应的TileRange中。参数max_height来自FTG中的GetMaxHeightValue方法。tile_nums来自成员mTileNums,tr_array数组则来自成员mTileRangeArray。
&1&int&height_unit&=&max_height&/&(tile_nums+<span style="color: #)&;&2&&&&&int&height_current&=&height_unit&;&3&&4&for(int&i=<span style="color: #;&i&tile_&i++)&5&&&&&{&6&&&&&&&&&tr_array[i].id&&&&&&&&&&&&&&=&i+<span style="color: #;&7&&&&&&&&&tr_array[i].optimalHeight&=&height_current&;&&8&&&&&&&&&tr_array[i].lowHeight&&&&&&=&height_current&-&height_unit&;&&9&&&&&&&&&tr_array[i].hightHeight&&&=&height_current&+&height_unit&;<span style="color: #&<span style="color: #&&&&&&&&&height_current+=height_<span style="color: #&<span style="color: #&&&&&}
&&& CalculateTilePresent( TileRange tr, int height_value )&&& 计算当前高度(参数height_value)在当前TileRange(参数tr)的可见度。height_value由FTG中的GetHeightValue方法指定,tr由mTileRangeArray中遍历到的元素指定。
&&& 根据前面讲述的算法,对当前TileRange可以划分出两个区间。加上正好位于optimal点的情况,还有位于边界之外的情况,共有4种常规的情况,这4种情况计算可见度的方法不同。但是,还要考虑到另外2中特殊的情况:那就是位于最底部TileRange的low-optimal区间和位于最顶部TileRange的optimal-high区间。这两种情况的区间,由于没有其他的TileRange与它们重叠,如果按照常规规则计算的话,那么就会仅仅减少可见度而不再混合其他任何像素,导致出现像素颜色趋于黑色(亮度减弱)。因此对这两种特殊的情况,应该不允许减小可见度,也就是要令返回值为100%。
&1&//考虑到边界&2&&&&&if(tr.id==mTileNums&&&&&height_value&&&tr.optimalHeight)&3&&&&&{&4&&&&&&&&&return&<span style="color: #;&5&&&&&}&6&&7&&&&&if(tr.id==<span style="color: #&&&&height_value&&&tr.optimalHeight)&8&&&&&{&9&&&&&&&&&return&<span style="color: #;<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&if(&height_value&==&tr.optimalHeight&)<span style="color: #&&&&&{<span style="color: #&&&&&&&&&return&<span style="color: #.0;<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&else&if(&height_value&&&tr.optimalHeight&&&&height_value&&&tr.lowHeight&)<span style="color: #&&&&&{<span style="color: #&&&&&&&&&return&(double)(height_value&-&tr.lowHeight)&/&(double)(tr.optimalHeight&-&tr.lowHeight)&;<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&else&if(&height_value&&&tr.optimalHeight&&&&height_value&&&tr.hightHeight&)<span style="color: #&&&&&{<span style="color: #&&&&&&&&&return&(double)(tr.hightHeight&-&height_value)&/&(double)(tr.hightHeight&-&tr.optimalHeight)&;<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&else&if(&height_value&&=&tr.lowHeight&||&height_value&&=&tr.hightHeight)<span style="color: #&&&&&{<span style="color: #&&&&&&&&&return&<span style="color: #.0;<span style="color: #&&&&&}
&&& GetTexCoords( CIMAGE* tile, uint* x, uint* z )&&& 这是处理对付任意Tile尺寸的一个简单方法。当提供的x和z坐标超出tile的实际坐标时,需要将xz坐标&#8220;绕回&#8221;到正确的位置上来。没什么复杂的东西,仅仅是用坐标去余除tile的尺寸即可。如此一来,你就可以提供任意的x和z坐标而不会发生任何&#8220;溢出&#8221;错误了。
<span style="color: #&&&& (*x)&=&(*x)&%&tile-&GetWidth();<span style="color: #&&&&&(*z)&=&(*z)&%&tile-&GetHeight();
&&&& &GetInterpolatedHeightValue(uint heightmap_size,uint texturemap_size, uint unscaled_x, uint unscaled_z)&&& 这是一个帮助你正确找到与纹理图像素对应的高度图像素值(即高度值)的方法。纹理像素根据自己在图中的位置,找到高度图中等比例位置的像素值,作为返回的高度值。这种方法可以提高读取精度。参数heightmap_size是高度图的边长,texture_size是纹理图的边长。unscaled_x和unscaled_z是纹理图的坐标位置,意为&#8220;未缩放的坐标&#8221;;在函数的内部,会算出一个&#8220;缩放后的坐标&#8212;&#8212;scaled_x和scaled_z&#8221;,这是对应的高度图坐标,用这对缩放后的坐标到高度图中查找高度。注意,这里说的纹理图都是指输出用的纹理图,不是作为&#8220;原料&#8221;的纹理图。不要混淆了。
&&& 在函数内部,首先会算出一个比率ratio。ratio= heightmap_size / texturemap_size 。然后就算出了&#8220;缩放后的坐标&#8221;。(scaled_x,scaled_z) = (unscaled_x,unscaled_z) * ratio 。 但是,仅仅算出scaled_x和scaled_z并不能解决问题。因为,ratio往往不是整数(我使用的是double类型)。因此scaled_x和scaled_z很有可能是小数。但是,怎么可能找到带有小数坐标的高度值呢?要知道,高度图的像素都是一格一格为单位的。因此,这里还需要有个手段进行进一步的模拟&#8212;&#8212;就是传说中&#8220;插值&#8221;。
&&& 我们知道scaled_x和scaled_z通常是带有小数的数。我们可以把它们拆成两部分:整数部分和小数部分。整数部分的坐标可以直接在高度图中取到高度值(标记为&#8220;起始点&#8221;高度);关于小数部分,将采用&#8220;线性插值&#8221;的办法计算得到&#8212;&#8212;很简单,再向后读一个像素的高度值(标记为&#8220;结束点&#8221;高度),测量起始点和结束点的高度差,然后将该高度差乘以小数部分,就得到了相应的高度值。再将此高度值加上起始点高度值,就得到了插值的高度值。当然,为了提到精确性,需要分别向右(x轴方向)和向下(z轴方向)各读取一个像素,因此就要进行两次这样的插值计算。最后,求两次插值的均值,才是最终的结果。
这里得到的高度值既不是60,也不是80,而是计算出来的72.5注意:这仅仅是x轴的。还需要对z轴做一遍相同的计算,最后求均值
&&& 在运算中还要考虑一种特殊的情况:当坐标位于纹理图的边界时(最右边或者最下边),那么就无法读取&#8220;后一个像素&#8221;了。这时,直接返回位于边界坐标对应的高度值即可。
&1&double&ratio&=&heightmap_size&/&texturemap_&2&&3&&&&&double&scaled_x&=&unscaled_x&*&ratio&;&&4&&&&&double&scaled_z&=&unscaled_z&*&ratio&;&&5&&6&&7&&&&&&8&&&&&double&height_atinterpolate_x&;&9&&&&&if((uint)scaled_x+<span style="color: #&&&mFTG-&GetSideLength())<span style="color: #&&&&&{<span style="color: #&&&&&&&&&height_atinterpolate_x&=&mFTG-&GetHeightValue((uint)scaled_x,(uint)scaled_z);<span style="color: #&&&&&}<span style="color: #&&&&&else<span style="color: #&&&&&{<span style="color: #&&&&&&&&short&height_atstart_x&&&&=&mFTG-&GetHeightValue((uint)scaled_x,(uint)scaled_z);<span style="color: #&&&&&&&&short&height_atend_x&=&mFTG-&GetHeightValue((uint)scaled_x+<span style="color: #,(uint)scaled_z);<span style="color: #&<span style="color: #&&&&&&&&height_atinterpolate_x&=&(&height_atend_x&-&height_atstart_x&)&*&(&scaled_x&-&(uint)scaled_x&)&+&height_atstart_x;&<span style="color: #&<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&<span style="color: #&&&&&double&height_atinterpolate_z&;<span style="color: #&&&&&if((uint)scaled_z+<span style="color: #&&&mFTG-&GetSideLength())<span style="color: #&&&&&{<span style="color: #&&&&&&&&&height_atinterpolate_z&=&mFTG-&GetHeightValue((uint)scaled_x,(uint)scaled_z);<span style="color: #&&&&&}<span style="color: #&&&&&else<span style="color: #&&&&&{<span style="color: #&&&&&&&&&short&height_atstart_z&&&&=&mFTG-&GetHeightValue((uint)scaled_x,(uint)scaled_z);<span style="color: #&&&&&&&&&short&height_atend_z&=&mFTG-&GetHeightValue((uint)scaled_x,(uint)scaled_z+<span style="color: #);<span style="color: #&<span style="color: #&&&&&&&&&height_atinterpolate_z&=&(&height_atend_z&-&height_atstart_z&)&*&(&scaled_z&-&(uint)scaled_z&)&+&height_atstart_z;&<span style="color: #&&&&&}<span style="color: #&<span style="color: #&&&&&short&consequese&=&(int)(height_atinterpolate_x&+&height_atinterpolate_z)&/&<span style="color: #;<span style="color: #&<span style="color: #&<span style="color: #&&&&&return&
实践收获:
&&& 1.我曾花了3天的时间来查找这么一种错误:纹理图生成了,看上去很逼真,可一旦贴在地表上,却总是不尽如人意:有的地方如同预想,高处为雪低处为土;但有的地方却不是这样,高处为土低处却出现了雪。这可怎么整?要知道引起这个问题的原因有千万中可能!我想了许许多多的方法来寻找问题到底出在哪里(包括下面说的一些调试方法,也缘于此)。我监视了每个像素的高度值、可见度、颜色值等等,并把它们输出到一个log文件中查看。最后,在我就要崩溃的瞬间,我想到了一种特别的方法来碰碰运气:用染色的方法来比对纹理图和高度图的异同,以此找出规律。我注意到高度图在非常低的地方像素颜色会很暗,因此我将纹理图对应的高度值低到一定程度(非常非常低)的像素都&#8220;染成&#8221;红色。然后我对比了两张图片。猜我看到了什么?&#8212;&#8212;你一定无法体会当我看到它们时的惊讶和激动。
&&&& &&& &&&&&&&&&&&&&&&&&&&&&&&& && 左:高度图&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 中:纹理图&&&&&&&&&&&&&&&&&&&&&&& 右:将高度图垂直翻转后,与纹理图混合对比的结果。从右图可以看出,二者的凹陷完全吻合
&&& 这时的结果再明了不过了。纹理图与高度图的方向竟然互相垂直反转!这是什么原因造成的?我突然想起了纹理图是一张BMP格式图片,而高度图是一张RAW格式图片。经过思考,终于明白了是由于特定图片格式的读写顺序不同造成的差异:RAW是从左到右、从上到下的写入顺序,因此它的原点坐标在图片的左上角;而BMP却比较特殊,它是从左到右,从下到上的写入顺序,因此原点坐标在图片的左下角!因此在写入纹理图像素时,一定得注意正确的z轴方位,先写最后一行,最后写第一行。ok,让我们问候微软全家以及他们的祖宗吧...
<span style="color: #&//注意第二个参数是side-1-z,此举即为z轴反序写入<span style="color: #&mTextureDataBuffer-&SetColor(x,side-<span style="color: #-z,(unsigned&char)red_data,(unsigned&char)green_data,(unsigned&char)blue_data);
&&&&&&&2.再次注意溢出。在调试GetInterpolatedHeightValue时,又发生了意想不到的错误。在很高的地方竟然齐刷刷地用黄土纹理覆盖了。根据多次遭蹂躏的经历,我很直觉地感觉到这又是一起&#8220;溢出&#8221;事故。用log输出数据&#8212;&#8212;果不其然!那么,在这个方法中,到底哪里溢出了呢?看下面的代码
//溢出的代码1&short&consequese&=&(short)(height_atinterpolate_x&+&height_atinterpolate_z)&/&<span style="color: #;
//正确的代码1&short&consequese&=&(int)(height_atinterpolate_x&+&height_atinterpolate_z)&/&<span style="color: #;
&&&&&&& 从中可以看到,在很高的地方,两个非常大的16位数相加,会导致结果超过2^16,但如果依然用16位存储中间结果,就会造成数据溢出,表示成负数。对于负数的纹理,程序当然会按照最下层Tile的low-optimal对待了。因此就变成了黄土纹理。只要改用较大的数据存储就不会溢出,被2除之后又会回到2^16的范围内,这时就还能用short类型存储了。
&&& 3.条件编译与调试。为了得到调试信息,我写了一个方法,用来专门输出调试数据(每个像素的高度值、可见度、颜色值等等)。但是,输出这么一个海量的数据,其等待时间不是一时半会的。我一会想看数据,一会又想看效果,怎么办?总不能看数据时写上输出log的代码,看效果时再删掉吧。我采用了&#8220;条件编译&#8221;的小技巧,看数据时就定义指定宏,看效果时就删掉这个宏。方便至极! &#8220;条件编译&#8221;真的是调试程序的必备手段啦!
&1&#ifdef&_DEBUG_OUTPUTDATA&2&&&&&&&&&//输出数据&3&&&&&&&&&OutputDataLog("--------------","--------------");&4&&&&&&&&&OutputDataLog("tile&id:",tr_array[i].id);&5&&&&&&&&&OutputDataLog("&&tile&low_height:",tr_array[i].lowHeight);&6&&&&&&&&&OutputDataLog("&&tile&optimal_height:",tr_array[i].optimalHeight);&7&&&&&&&&&OutputDataLog("&&tile&hight_height:",tr_array[i].hightHeight);&8&&&&&&&&&OutputDataLog("--------------","--------------");&9&&&&&&&&&OutputDataLog("&","&");<span style="color: #&#endif&//&_DEBUG_OUTPUTDATA<span style="color: #&
&&& 4.模板的利用。我从来没有写过模板与泛型的东西(我从来不掩饰自己是菜鸟),直到我写输出调试数据的方法时。我想达到一种效果,就是提供的参数不要指定是什么类型,因为我需要输出各种各样的类型(string,int,short,double等等)。这时模板帮了我大忙。第一次体会到模板的强大,还等什么,写字留念~
&1&template&typename&T&&2&inline&void&OutputDataLog(const&std::string&&tip_text,&T&data&)&3&{&4&&&&&if(ofs.is_open())&5&&&&&{&6&&&&&&&&&ofs&&tip_&7&&&&&&&&&ofs&&"&:&";&8&&&&&&&&&ofs&&&9&&&&&&&&&ofs&&std::<span style="color: #&&&&&}<span style="color: #&&&&&else<span style="color: #&&&&&{<span style="color: #&&&&&&&&&std::runtime_error("can't&open&file:&data_log.txt&");<span style="color: #&&&&&}<span style="color: #&&&&&<span style="color: #&}
&& 我真的不想再浪费一滴口水在这篇文章上了。从此以后我也要慎重考虑,是否再写类似这样的冗长的臭文了。最后我要对你表示感谢,因为你能坚持看到这个地方,简直可以算做我的粉丝(或者偶像,知己...随便怎么称呼)。感谢你对我劳动成果的尊重,我也希望本文对你有帮助。如果你考虑转载请附上谢谢。同时我会把它视作我对学过知识的总结,以防落得没人看出现的尴尬.....好了,让我们赶紧结束它吧。再见。}

我要回帖

更多关于 简单加密算法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信