本文主要講的是《天龍八部》游戲的地形和一部分場(chǎng)景的具體實(shí)現(xiàn),使用C++, Ogre1.6,我摸索了段時(shí)間,可能方法用的并不是最好的,但好歹實(shí)現(xiàn)了。文章可能講得有點(diǎn)羅嗦,很多簡(jiǎn)單的東西都講了。我是修改了ETM(Editable Terrain Manager)實(shí)現(xiàn)的地形,其實(shí)單單實(shí)現(xiàn)天龍八部的地形場(chǎng)景等的載入根本不需要使用ETM,直接用Ogre的頂點(diǎn)->索引->紋理就可以搞定地形,但我要做的是可以實(shí)時(shí)編輯的,所以用了ETM,場(chǎng)景其由于很重要的粒子和model等部分我還沒(méi)去看,所以等以后看了再詳細(xì)寫(xiě)關(guān)于場(chǎng)景的部分,但這個(gè)Demo已經(jīng)實(shí)現(xiàn)了基本的場(chǎng)景的載入。光,霧,環(huán)境,靜態(tài)物等都能載入。
修改過(guò)的ETM和這個(gè)場(chǎng)景的Demo代碼可以通過(guò)文章底下的鏈接下載。
Demo截圖如下:(少林)
這個(gè)Demo比較簡(jiǎn)單,只能移動(dòng)攝像機(jī)看看場(chǎng)景。
我研究這些的動(dòng)機(jī)是當(dāng)前在學(xué)校做一個(gè)網(wǎng)游項(xiàng)目,想做得類似于《Second Life》,苦于沒(méi)有游戲美工,最近有馬上要二期驗(yàn)收了,為了讓游戲看上去光鮮一點(diǎn),無(wú)奈之下只好借《天龍八部》的資源來(lái)用了。看了不少大牛的博客,將得感覺(jué)都有點(diǎn)不是很詳細(xì),只是大概把文件格式講了一下而已,具體怎么實(shí)現(xiàn)說(shuō)得不多(可能是覺(jué)得實(shí)現(xiàn)太容易,懶得多說(shuō)了吧...)最主要的是,似乎沒(méi)看到有人發(fā)完整的代碼。
實(shí)際項(xiàng)目中用的程序代碼我就不放出來(lái)了,場(chǎng)景部分差不多,只是多了個(gè)內(nèi)建的編輯器,人物移動(dòng)和網(wǎng)絡(luò)通信部分等。
編輯器的截圖曬一下,功能還不全 :-)
言歸正傳,先簡(jiǎn)單地說(shuō)一下載入一個(gè)天龍八部場(chǎng)景的大致過(guò)程:
- 讀取.Scene文件
- 根據(jù)<Texture>讀取.Terrain文件
- 讀取地磚大小(<tileSize>) 地形大小(xsize, ysize),縮放值(<scale>),地圖中心坐標(biāo)(<center>)。
- 讀取所有要用的地形貼圖(<textures>中各項(xiàng))。
- 讀取.gridinfo 文件,此文件中存放著每個(gè)格子對(duì)應(yīng)的紋理坐標(biāo)。
- 根據(jù)3,4,5步的信息用修改過(guò)的ETM創(chuàng)建Terrain。
- 讀取lightmap, 是png格式的預(yù)處理的場(chǎng)景陰影圖。
- 讀取場(chǎng)景中的各種模型等,并插入到場(chǎng)景Root中。
(注:天龍八部的場(chǎng)景包含很多個(gè)文件,用“劒蚩”的資源提取工具提取出來(lái),文件夾下的基本都是,但我暫時(shí)不考慮尋路,碰撞等,所以就地形來(lái)講只研究.Terrain文件,.Gridinfo文件。 資源提取的問(wèn)題可訪問(wèn)http://www.cnitblog.com/sword/category/5167.htmlScene )
下面我分幾個(gè)部分來(lái)具體講如何實(shí)現(xiàn)天龍八部的場(chǎng)景Demo。
讀取高度圖
做地形首先肯定是要讀取高度圖,《天龍八部》的高度圖是保存在.Heightmap文件中,讀取的方法是跳過(guò)前面8個(gè)字節(jié),讀地形的width和height,然后讀取width*height個(gè)float型數(shù)據(jù),上面說(shuō)到.Terrain文件中有地形大小(xsize, ysize),縮放值(<scale>),地圖中心坐標(biāo)(<center>),<scale>中有xyz 3個(gè)值(一般情況下是100,100,100),分別是x,y,z軸的放大系數(shù),用ETM創(chuàng)建地形的時(shí)候,直接用讀取到的float型數(shù)據(jù)作為高度圖數(shù)據(jù),然后再用上面那些值作為參數(shù),定義地形的大小,縮放值,和偏移。
這是讀取高度圖的代碼,heightMapData是float型的數(shù)組,存放原始的高度圖信息。
void TileTerrainInfo::LoadHightMap( const char* fileName, const char* type )
{
FILE* pf = fopen( fileName, "rb" );
fseek( pf, 8, SEEK_SET );
int height, width;
fread( &width, 4,1, pf );
fread( &height, 4,1, pf );
assert( height = this->height+1 );
assert( width == this->width+1 );
if( heightMapData )
delete []heightMapData;
heightMapData = new float[height*width];
for( int i = 0; i < height; ++i )
{
for( int j = 0; j < width; ++j )
{
float data;
fread( &data, 4,1,pf );
heightMapData[i*width+j] = data;
}
}
fclose( pf );
}
材質(zhì)文件的分析
我想先講一下地形的材質(zhì),因?yàn)橛脛e人的資源,首先要知道怎么用這些資源,一般情況下材質(zhì)信息可以明顯地反映出如何使用紋理資源(不排除有可能用代碼動(dòng)態(tài)生成材質(zhì))。
在每個(gè).Terrain文件的最下面,有這些內(nèi)容。
<materials>
<template material="Terrain/OneLayer" name="OneLayer"/>
<template material="Terrain/OneLayerLightmap" name="OneLayerLightmap"/>
<template material="Terrain/TwoLayer" name="TwoLayer"/>
<template material="Terrain/TwoLayerLightmap" name="TwoLayerLightmap"/>
<fog_replacement exp="Terrain/OneLayer_ps%fog_exp" exp2="Terrain/OneLayer_ps%fog_exp2" linear="Terrain/OneLayer_ps%fog_linear" none="Terrain/OneLayer_ps"/>
<fog_replacement exp="Terrain/TwoLayer_ps%fog_exp" exp2="Terrain/TwoLayer_ps%fog_exp2" linear="Terrain/TwoLayer_ps%fog_linear" none="Terrain/TwoLayer_ps"/>
<fog_replacement exp="Terrain/OneLayerLightmap_ps%fog_exp" exp2="Terrain/OneLayerLightmap_ps%fog_exp2" linear="Terrain/OneLayerLightmap_ps%fog_linear" none="Terrain/OneLayerLightmap_ps"/>
<fog_replacement exp="Terrain/TwoLayerLightmap_ps%fog_exp" exp2="Terrain/TwoLayerLightmap_ps%fog_exp2" linear="Terrain/TwoLayerLightmap_ps%fog_linear" none="Terrain/TwoLayerLightmap_ps"/>
</materials>
定義了一些材質(zhì)模板。
我沒(méi)有深究其他的,只考慮TwoLayerLightmap這個(gè)材質(zhì)。
不記得是在哪個(gè)文件夾下,有一個(gè)文件FairyTerrain.material,其中就是地形的材質(zhì)。
我修改了一些內(nèi)容,將<lightmap>設(shè)tex_coord = 0. <layer0>設(shè)tex_coord=1,<layer1>設(shè)tex_coord=2。這是因?yàn)槲蚁胱孍TM原有的地形和天龍八部的地形共存,而原有地形紋理坐標(biāo)剛好和<lightmap>紋理坐標(biāo)相符合,所以設(shè)為同一層。
這是我改過(guò)的材質(zhì)
material Terrain/TwoLayerLightmap
{
technique
{
pass
{
fragment_program_ref Terrain/TwoLayerLightmap_ps
{
}
texture_unit
{
texture_alias <layer0>
texture <layer0>
tex_address_mode clamp
tex_coord_set 1
}
texture_unit
{
texture_alias <layer1>
texture <layer1>
tex_address_mode clamp
tex_coord_set 2
}
texture_unit
{
texture_alias <lightmap>
texture <lightmap>
tex_address_mode clamp
tex_coord_set 0
}
}
}
}
<layer0>,<layer1>,<lightmap>是一個(gè)pass中的3個(gè)texture_unit.也就是3層紋理。顧名思義<layer0>是第一層紋理,<layer1>是第二層紋理,<lightmap>是光照?qǐng)D紋理(陰影圖),具體如何使用,如何使天龍八部的地形貼圖資源對(duì)應(yīng)到layer0,layer1,我下面會(huì)講到。
從FairyTerrain.cg中我們可以找到對(duì)應(yīng)的shader。
void TwoLayerLightmap_ps(
in float2 uv0 : TEXCOORD0,
in float2 uv1 : TEXCOORD1,
in float2 uvLightmap : TEXCOORD2,
in uniform sampler2D layer0,
in uniform sampler2D layer1,
in uniform sampler2D lightmap,
in float4 diffuse : COLOR0,
in float4 specular : COLOR1,
out float4 oColour : COLOR)
{
float4 c0 = tex2D(layer0, uv0);
float4 c1 = tex2D(layer1, uv1);
float3 texturedColour = lerp(c0.rgb, c1.rgb, c1.a);
float4 lightmapColour = tex2D(lightmap, uvLightmap);
float4 baseColour = diffuse * lightmapColour;
float3 finalColour = baseColour.rgb * texturedColour + specular.rgb * (1-c0.a) * (1-c1.a) * lightmapColour.a;
float3 resultColour = Fogging(finalColour);
oColour = float4(finalColour, baseColour.a);
}
很容易看出其大致思路是<layer1>的alpha值控制<layer0>和<layer1>進(jìn)行混合。
可見(jiàn),天龍八部的地形應(yīng)該是部分像魔獸一樣的格子式地形,部分權(quán)重圖地形,也就是ETM原有的那種貼圖模式,很多層紋理,然后又1-2層手動(dòng)生成的紋理數(shù)據(jù)控制各層紋理的alpha值,達(dá)到混合的效果,只不過(guò)這里是只有一個(gè)alpha通道來(lái)控制紋理混合。
兩層紋理的效果比單獨(dú)一層紋理好的多,我用OneLayerLightmap材質(zhì)試過(guò),效果比較赫人...
地形紋理的實(shí)現(xiàn)
<lightmap>紋理很明顯,是一整張紋理貼到整個(gè)地形,沒(méi)什么好說(shuō)的。
但<layer0> <layer1>這兩層地形紋理應(yīng)該怎么貼上去呢?
對(duì)于材質(zhì)中的這兩層紋理,有兩種可能。
1.<layer0>,<layer1>本身只是材質(zhì)模板中紋理的名字,沒(méi)有實(shí)際意義,在實(shí)際的程序中會(huì)為每一塊地形從材質(zhì)模板繼承一個(gè)模板,然后修改材質(zhì)中紋理的名稱。
2.在程序中手動(dòng)創(chuàng)建<layer0>,<layer1>。
先說(shuō)兩種不能實(shí)現(xiàn)的方法:
1. 在程序中手動(dòng)創(chuàng)建<layer0>,<layer1>, 為極大極大的貼圖(和真實(shí)的地形一樣大),該貼圖根據(jù).Terrain和.Gridinfo中的信息來(lái)組成,和lightmap一樣,整個(gè)貼到地形上。
在小游戲,只有可能一個(gè)屏幕那么大的地形,也許可以用,而且效果可能不錯(cuò),但在這種地形相對(duì)較大的游戲中是不可能的,首先,極大的浪費(fèi)資源,一個(gè)地磚的紋理,可能被用到幾十次幾百次,那么在這個(gè)大紋理中,就會(huì)有幾百個(gè)地磚紋理的拷貝,其次,不可能創(chuàng)建這么大的紋理(硬件不支持?反正我試過(guò)創(chuàng)建不出來(lái)..)
2. 像ETM一樣,將所有要用到的紋理(假設(shè)有n張)一個(gè)一個(gè)作為texture_unit放在材質(zhì)里面,然后用n/4張手動(dòng)生成的紋理去控制這些紋理的alpha值。這個(gè)方法不是對(duì)于天龍八部的地形不是很現(xiàn)實(shí),一般每個(gè)天龍八部的地形有大概十幾個(gè)不同的紋理,如果用這個(gè)方法,每個(gè)pass一般支持8個(gè)texture_unit,十幾個(gè)紋理,加n/4張控制紋理需要3-4個(gè)pass,效率似乎... 而且我們通過(guò)天龍的材質(zhì)文件可以看出,游戲應(yīng)該不是用的這個(gè)方法來(lái)實(shí)現(xiàn)的。
3. 每一個(gè)格子都有自己獨(dú)自的材質(zhì),修改每個(gè)格子材質(zhì)中的<layer0>, <layer1>, 改為它需要的材質(zhì)文件,如 "05武當(dāng)\褐色土地底層.jpg” 相當(dāng)于將每一個(gè)格子作為單獨(dú)的mesh。這個(gè)是可以實(shí)現(xiàn)的,我試過(guò),將ETM的TileSize設(shè)為1,然后生成每個(gè)Tile的時(shí)候修改其材質(zhì),成功了,地形也顯示出來(lái)了,完全正確,但幀率..... 呵呵,debug模式下fps 大于0小于1... 到release也許可以到十幾吧,我沒(méi)試,顯然是不能這樣搞的...
我最后實(shí)現(xiàn)地形貼圖用的是texture atlas,手動(dòng)創(chuàng)建一張紋理,將所有要用到的地形紋理組合成一張大紋理,然后每一個(gè)頂點(diǎn)設(shè)基于這張大紋理的UV坐標(biāo),texture atlas比每個(gè)格子設(shè)材質(zhì)更好的原因很顯而易見(jiàn),具體可以參考附件中所帶的文章,《“Batch, Batch, Batch:”What Does It Really Mean?》中的第30頁(yè):Batch Breaker: Texture Change.
下圖就是將wudang.Terrain中
<textures>
<textures>
<texture filename="03南海/巖石海礁01.jpg" type="image"/>
<texture filename="03南海/巖石海礁03.jpg" type="image"/>
<texture filename="05武當(dāng)/褐色土地底層.jpg" type="image"/>
<texture filename="05武當(dāng)/褐色土地上層.tga" type="image"/>
<texture filename="05武當(dāng)/青磚地底層.tga" type="image"/>
<texture filename="13鏡湖/鏡湖桃花瓣.tga" type="image"/>
... ...
</textures>
所定義的所有紋理組合成的一張大紋理。
可以發(fā)現(xiàn),天龍八部中的地形貼圖大小是不同的,但最大是256x256(就我目前所知),所以我干脆將每一格劃為256x256,共可容納有ROW_SIZExCOL_SIZE張小貼圖,這樣大貼圖的大小應(yīng)該是256*COL_SIZE x 256*ROW_SIZE。
我這臺(tái)機(jī)器支持的最大紋理大小似乎為4096x4096,那么理論上因該可以最多容納16*16張小貼圖,綽綽有余了。這樣雖然浪費(fèi)一點(diǎn)空間,但可以很方便地通過(guò)ID索引貼圖坐標(biāo)。
比如 <pixmap bottom="0.2480469" left="0.00390625" right="0.4960938" textureId="2" top="0.001953125"/> 通過(guò)這樣一塊pixmap的定義,我們可以根據(jù)textureId=2找到它所所在的位置。
其所在行為textureId/COL_SIZE,所在列為textureId%COL_SIZE。如上面那張大紋理的COLE_SIZE = 8(一行有8張小貼圖)
所以textureId=2的這張小貼圖所在行row=0,坐在列col=2.
我們知道紋理坐標(biāo)范圍為0.0f-1.0f,所以textureId=2的小貼圖左上角的UV坐標(biāo)為U = (float)col/COL_SIZE = 0.25f , V = (float)row/ROW_SIZE = 0.0f.
再根據(jù)pixmap中的信息left ,right, top, bottom 可以計(jì)算出小貼圖四個(gè)點(diǎn)的坐標(biāo)。在創(chuàng)建頂點(diǎn)時(shí)將紋理坐標(biāo)附上即可。
具體的過(guò)程應(yīng)該是
1.手動(dòng)創(chuàng)建名字為<layer0>的texture
代碼如下:
TexturePtr layer0 = TextureManager::getSingletonPtr()->createManual(
"<layer0>", "General",TEX_TYPE_2D,
layerTextureWidth,layerTextureHeight, 1, 3, PF_BYTE_RGBA, TU_WRITE_ONLY );
2. 將材質(zhì)中的texture_unit <layer1>中的texture_name 由<layer1>改為<layer0>,因?yàn)槲覀儍蓪佑玫氖峭粡埣y理,沒(méi)必要復(fù)制一遍,直接改名指向同一張紋理就行了。
代碼如下:
MaterialPtr material (MaterialManager::getSingleton().getByName("Terrain/TwoLayerLightmap"));
material->getTechnique(0)->getPass(0)->getTextureUnitState( 1 )->setTextureName( "<layer0>");
3.讀取.Texture文件,將要用到的紋理拼接為大紋理,如上面那張圖。
地形的頂點(diǎn)與索引
若地圖為192x192,它就是應(yīng)該有192*192個(gè)格子。一般情況下的做法下,它應(yīng)該有193*193個(gè)頂點(diǎn),織成一個(gè)網(wǎng)狀,但由于我用的atlas,
可以知道,每一個(gè)非邊緣的頂點(diǎn)將會(huì)有4個(gè)紋理坐標(biāo)(左上,右上,左下,右下 )
如下圖
中間的頂點(diǎn)要同時(shí)負(fù)責(zé)A塊的右下,B塊的左下,C塊的右上,D塊的左上。
話說(shuō)一個(gè)頂點(diǎn)確實(shí)是同時(shí)又多個(gè)紋理坐標(biāo)的,只要設(shè)置不同的tex_coord。但天龍八部地形貼圖一般有3層,<layer0>,<layer1>,<lightmap>,分別是兩層地形,一層預(yù)處理的陰影。
一層<lightmap>不用多說(shuō)的,就是一張大紋理,每個(gè)頂點(diǎn)的坐標(biāo)是u=col/terrainColSize, v=row/terrainRowSize.
另外兩層就是我們需要考慮的,因?yàn)橛袃蓪樱@樣每個(gè)點(diǎn)不止同時(shí)負(fù)責(zé)4塊,要同時(shí)負(fù)責(zé)兩層共8塊,這樣這個(gè)pass的8個(gè)texture_unit都滿了,必須再用一個(gè)pass來(lái)做<lightmap>那一層,效率不行。
所以只好用另一方法,就是在非邊緣的每一個(gè)位置,將4個(gè)頂點(diǎn)重合在一起,這4個(gè)頂點(diǎn)的紋理坐標(biāo)不同,但位置相同,即每一個(gè)格子都有四個(gè)獨(dú)立的頂點(diǎn),相鄰的兩個(gè)格子有兩個(gè)點(diǎn)重合。
也就是說(shuō)192x192的地圖,需要有192*192*4個(gè)頂點(diǎn)。索引方式還是差不多,每一個(gè)格子需要6個(gè)索引,所以一共要192*192*6個(gè)索引。
這樣,ETM中生成頂點(diǎn)和索引的部分代碼都需要改,生成頂點(diǎn)的代碼在void Tile::createVertexData(size_t startx, size_t startz)中,生成索引的代碼在void Tile::createIndexData()中。
天龍八部的.Terrain文件一般有這么一行<scale x="100" y="100" z="100"/>,說(shuō)明地形在3個(gè)方向都是放大100倍,x,z本是一格大小為1x1為單位的,放大后即為100x100,
一個(gè)192x192的地形實(shí)際游戲中的大小應(yīng)該為19200*19200,而天龍的坐標(biāo)系是正中間坐標(biāo)為.Terrain文件中的<center>的值,若不存在則中心為(0,0), 正方向?yàn)檎?fù)方向?yàn)樨?fù),所以當(dāng)<center>值為(0,0),192x192的地形實(shí)際坐標(biāo)范圍應(yīng)該是 (-9600,-9600)到(9600,9600)。
要注意的是不是將所有頂點(diǎn)作為一個(gè)mesh,而是應(yīng)該根據(jù).Terrain文件中的<tileSize>規(guī)定每一個(gè)TerrainTile的大小,每個(gè)TerrainTile中包含tileSize x tileSize 個(gè)地形網(wǎng)格,一個(gè)TerrainTile作為一個(gè)Entity插入到一個(gè)場(chǎng)景節(jié)點(diǎn)。
頂點(diǎn)位置和索引考慮完了,該是要考慮每個(gè)頂點(diǎn)的紋理坐標(biāo)的問(wèn)題了。
要給每個(gè)頂點(diǎn)設(shè)UV必須用到.Gridinfo文件中的信息,該文件中定義了每一個(gè)格子對(duì)應(yīng)的紋理信息。
具體的文件格式可參考,我在這就不贅述了。
http://m.shnenglu.com/mybios/archive/2009/07/26/91267.html
此處是正解,其他地方似乎多多少少都有錯(cuò),特別是op=8的時(shí)候,要注意是與對(duì)角線兩邊的兩個(gè)點(diǎn)(不是對(duì)角線上的點(diǎn))從上面復(fù)制到下面。
如圖1應(yīng)該是將左上角的頂點(diǎn)紋理坐標(biāo)復(fù)制到右下角
圖2應(yīng)該是將右上角的紋理坐標(biāo)復(fù)制到左下角。

圖1 圖2
但還有一處我有不同,op=4的時(shí)候,我覺(jué)得應(yīng)該是順時(shí)針轉(zhuǎn)90度,測(cè)試下來(lái)似乎沒(méi)問(wèn)題。
這是我的根據(jù)op操作UV坐標(biāo)的代碼,op=4的時(shí)候我貌似確實(shí)是在順時(shí)針轉(zhuǎn)吧……
void changeGridInfoUV( AutoTexCoord& leftTop, AutoTexCoord& rightTop, AutoTexCoord& leftBottom, AutoTexCoord& rightBottom, uchar state, bool bIndex )
{
//0 不變
//1 圖片水平翻轉(zhuǎn)
//2 圖片垂直翻轉(zhuǎn)
//4 順時(shí)針旋轉(zhuǎn)度
//8 對(duì)角線上方頂點(diǎn)紋理坐標(biāo)復(fù)制到對(duì)角線下方頂點(diǎn)。(與對(duì)角線垂直的兩個(gè)頂點(diǎn))
uchar res1 = state&1;
uchar res2 = state&2;
uchar res3 = state&4;
uchar res4 = state&8;
if( res1 != 0 )
{
leftTop.Exchange( rightTop );
leftBottom.Exchange( rightBottom );
}
if( res2 != 0 )
{
leftTop.Exchange( leftBottom );
rightTop.Exchange( rightBottom );
}
if( res3 != 0 )
{
leftTop.Exchange( rightTop );
leftBottom.Exchange( rightTop );
rightBottom.Exchange( rightTop );
}
if( res4 != 0 )
{
// 非正常索引
if( bIndex ) {
(leftBottom.setX( rightTop.getX) );
leftBottom.setY( rightTop.getY() );
}
// 正常索引
else {
rightBottom.setX( leftTop.getX() );
rightBottom.setY( leftTop.getY() );
}
}
}
讀取場(chǎng)景環(huán)境與模型
先階段我讀取了一部分場(chǎng)景,包括環(huán)境和一些模型,粒子等部分還沒(méi)看,所以這個(gè)場(chǎng)景是不完整的,不過(guò)大概的輪廓都出來(lái)了。
讀取場(chǎng)景其實(shí)就是用TinyXML讀取.Scene中的各種XML項(xiàng),然后根據(jù)讀取的數(shù)據(jù)創(chuàng)建相應(yīng)的場(chǎng)景節(jié)點(diǎn),或設(shè)置相應(yīng)的場(chǎng)景環(huán)境,如霧,skydome等。
具體代碼下載附件看吧,有點(diǎn)無(wú)聊,都是switch-case語(yǔ)句。
但有一點(diǎn)一定要注意,在讀取資源前一定要先調(diào)用一個(gè)函數(shù)
setlocale( LC_CTYPE, "" );
不然中文路徑或文件名的.mesh文件是讀不了的。
地形和場(chǎng)景都搞定了,可以看看結(jié)果了!然而,悲劇出現(xiàn)了!
遠(yuǎn)景的效果圖,很明顯,地形一格一格像有裂縫一樣……

近處的效果圖, 近了以后,就沒(méi)地形的裂縫了……
地形裂縫問(wèn)題
問(wèn)了一下我的一個(gè)學(xué)長(zhǎng),自己也思考些時(shí)間。估計(jì)是由于是用的atlas texture, 然后一定距離后的mipmap和texture filtering,產(chǎn)生了裂縫的問(wèn)題。
通過(guò)附件中的《Improve Batching Using Texture Atlases 》在Applying Texture Filtering To Atlases節(jié)可以看討論到texture filtering對(duì)texture atlas造成的影響,但是解決方案只是理論上的,并沒(méi)有實(shí)現(xiàn),一個(gè)是寫(xiě)shader,在不同的mipmap下調(diào)整紋理坐標(biāo),另一個(gè)是預(yù)留紋理,就是在已有紋理上加一圈和邊緣相同的像素。文章中還提到:enabling anisotropic filtering minimizes these errors,我試了一下,設(shè)置了filtering anisotropic , 并且將anisotropic_max 設(shè)為最大,結(jié)果卻是略有好轉(zhuǎn),但幀率損失了50fps左右… 也許是我的顯卡太水了?我最后還是沒(méi)采用這個(gè)解決方案。
我最后的解決方案是
1,手動(dòng)創(chuàng)建紋理的時(shí)候,設(shè)置其mipmap的級(jí)別最多為3,這樣就不會(huì)有更高級(jí)別的mipmap,導(dǎo)致更嚴(yán)重的失真。
2,手動(dòng)創(chuàng)建mipmap,而不是讓其自動(dòng)生成。比如原來(lái)是1024*1024大小的貼圖,1級(jí)mipmap的大小應(yīng)該為512*512,默認(rèn)它是自動(dòng)生成的,我設(shè)為手動(dòng)生成,先把個(gè)小貼圖縮小為50%,再把他們組成一張512x512的貼圖作為原來(lái)的貼圖的mipmap,一次類推手動(dòng)生成3層mipmap.這樣情況稍微有了點(diǎn)改善。
3,小貼圖合成大貼圖之前,將他們統(tǒng)一resize到256*256的大小,在組合成大貼圖,這樣后感覺(jué)裂縫問(wèn)題好轉(zhuǎn)了不少。
4,預(yù)留紋理坐標(biāo),雖然天龍八部開(kāi)始就預(yù)留了紋理坐標(biāo),可以.Terrain文件中的紋理坐標(biāo)很多都不是絕對(duì)的0.0f 0.25f 0.5f 等,都是一些 0.2480469 ,0.00390625,0.4960938等,我不知道它本是出于什么原因。但我用了這樣的坐標(biāo)還是不行,還是要把它設(shè)地更加靠?jī)?nèi)才能避免裂縫。
所以讀取紋理坐標(biāo)的時(shí)候加上了一個(gè)fixFloat的過(guò)程,這個(gè)過(guò)程很不科學(xué),但我試了一下似乎有點(diǎn)用處就用上了。
static void fixFloat( float& f )
{
if( f < 0.01f )
f = 0.005f;
else if( f > 0.24f && f < 0.25f )
f = 0.245f;
else if( f > 0.25f && f < 0.26f )
f = 0.255f;
else if( f > 0.49f && f < 0.50f )
f = 0.495f;
else if( f > 0.5f && f < 0.51f )
f = 0.505f;
else if( f > 0.74f && f < 0.75f )
f = 0.745f;
else if( f > 0.75f && f < 0.76f )
f = 0.755f;
else if( f > 0.99f )
f = 0.995f;
}
這樣也在一定程度上改善了裂縫的問(wèn)題,但有的貼圖看上去會(huì)不太吻合,但總比裂縫好。
隨后地形基本上看不出有裂縫了,但還是有一點(diǎn)痕跡。

這個(gè)地形裂縫的問(wèn)題困擾了我許久,最后的解決方案我也覺(jué)得不甚滿意,不知道有哪位有好點(diǎn)的解決方案請(qǐng)告訴我。