World Craft中的無縫地形實現 v1.0

來源:互聯網
上載者:User
由于格式原因,粘貼出來的效果不好,可以下載pdf版本:

http://download.csdn.net/source/643603

 World Craft中的無縫地形實現 v1.0

熊家煜,QY_TEK

Emile:Xjyhust@gmail.com

首頁:http://blog.csdn.net/xjyhust

簡單說明:

這份文檔不是論文,我稱之為筆記,或者.note;這份文檔free for all,你可以隨便使用。如果你想引用,請註明出處http://blog.csdn.net/xjyhust,以及作者 熊家煜。 Ok,由於這個文檔只是v1.0,所以缺少很多實際的測試結果,在後面的版本中我會加進來;但是裡面的技術是不會有太大的變化的。最後,希望這個文檔對你有用。

無縫地形技術簡介

mesh部分,使用分塊的高度圖來儲存資訊;紋理部分,每塊地形使用8張diffuser紋理,4張normal map紋理,使用調色盤紋理查詢權值進行混合。分別對待情境編輯器和引擎運行時的地形處理方法,使效率和靈活性都能得到保障。

無縫網格的實現

地形的實現,主要針對2D的高度圖,儲存的精度是float 32bit。為了支援超大的無縫地形,必須將地形進行分塊,在運行期間,記憶體中只儲存可視的一塊地區。

LOD的實現

地形分塊以後的最小單位,我們先稱之為brick。離攝像機較近的brick,有較為細節的lod,定義這個時候的lod為低lod;離攝像機較遠的brick,有較為粗略的lod,定義這個時候的lod為高lod。(不要弄錯了,近處lod低,遠處lod高。)

Lod的大致方法就是,lod為level 0時,渲染每個頂點;level為1時,每2個頂點渲染一個頂點;level 為i時,每(2的i次方)個頂點渲染一個頂點...... 具體參看:

    Level i                         Level i + 1

為瞭解決地形“龜裂”的問題,需要對不同level之間缺少的頂點進行處理。有幾種不同的處理方式,我的方式是:

假設這個時候level i和level i+1相接,我將level i+1的brick和level i-1臨界的那條邊界上缺少的頂點加入原有的三角形列表中:

為瞭解決這個邊界“龜裂”的問題,我們需要有不同的三角帶。由於在World Craft中,lod的分布都是按照環形的:

所以每個lod的三角帶需要有這樣五種不同的情況,對於level 為i的brick。情況一,他只和level為i的brick相接;情況二至五,他有且僅有一側和level i-1的brick相接。

效能和最佳化

對於brick渲染的最佳化,我們引入了一個sheet的概念。Sheet是一個更大的劃分單位,在我的實踐過程中,sheet為4*4個brick或者8*8個brick組成(根據brick的大小不同),sheet共用一個vertex buffer,這樣可以使效率更高一些,雖然也是draw n次,但是不需要修改vertex buffer,只要修改index buffer,對待lod相同的brick,index buffer也是一樣的,只需要修改位移量。引入sheet,也是為了後面的texture的最佳化需要,不需要頻繁的切換material,也不用寫過於複雜的sort就可以達到最佳化的目的。

編輯器和引擎運行時的不同

有這樣幾個原因使我們有理由分開對待這兩種情況:

1. 編輯器需要及時的相應使用者的輸入,這就要求每一次對地形的改變在較短時間內完成

2. 引擎不需要靈活性,所以渲染的資料需要盡量的最佳化

編輯器中沒有sheet的實現(只有概念),即每個brick有單獨的vertex buffer,brick要儘可能的小,比如,17*17大小。這樣做大大減小了vertex buffer的修改範圍,使用者的修改會及時得到反饋,不會出現太長的“假死”。

而引擎運行時,sheet的大小為513*513或者257*257,每個brick的大小為65*65或者33*33。我比較傾向513*513的sheet和33*33的brick。(具體是多少比較合理,在這份.note裡面我沒有列出詳細的比較資料,在v2.0裡面會列出一些測試的實際資料)

World Craft的地形編輯

World Craft中,使用一個較大的height map來表示一個sheet的,比如513*513大小的height map,然後每個brick的大小為17*17,對應height map的某個17*17的地區,由於brick單獨使用vertex buffer,所以,相鄰的brick之間共用height map上的17個pixel。

以rise/lower地形筆刷為例,具體的流程如下:

1. 判斷滑鼠拾取點(和地形相交的點)

2. 計算拾取點所對應的height map上的點

3. 將筆刷(一樣也是32bit-float的位元影像)正確的操作到height map上

4. 更新所影響到的地區(只更新地區內的brick)

有幾點需要注意的地方:

1. 更新brick的時候,只更新頂點的position,而不更新normal,否則速度會很慢;normal的更新只在每次滑鼠抬起的時候進行;最後的效果非常不錯。

2. 塗刷height map的時候,引入一個筆刷密度的概念,表示每隔多少像素才進行一次塗刷;當滑鼠移動過快或者過慢的時候,不會產生不均勻的效果。大概的方法就是:記錄每次的圖刷點,如果圖刷點之間的累計距離超過了筆刷密度的定義,就在最近的塗刷路徑上的正確地方進行塗刷。

3. 頂點normal的更新,是自己寫得,沒有用到nvidia的nvMeshMender庫,不知道為什麼,用nvMeshMender的時候速度奇慢,也許是網格類型的mesh的頂點重複比較多。

頂點normal的計算

如,對於每個頂點vert(圖中的黑點),計算其周圍的6個三角形的normal,然後取平均值。我不知道這個是不是正確的演算法,反正最後的效果是正確的。 ;)

無縫紋理的實現紋理調色盤

紋理調色盤的做法就是:調色盤紋理上的點對應的rgba值作為權值,分別表示使用4張不同紋理的權值。如果調色盤上的點只有紅色,那麼就是表示最後地形上面的該點只使用了第一張紋理;如果調色盤的點有4中不同的顏色,分別為0.1,0.2,0.3,0.4,地形上最後的顏色就是0.1份紋理1,0.2份紋理2,0.3紋理3,0.4份紋理4的混合。具體的演算法後面有hlsl給出。

為什麼這樣做,而不是使用一張全域的紋理?具體原因我之前的一篇blog也說道了,這裡我直接粘貼過來:

簡單的重複紋理是現在最常用的方法,但是效果一般。使用全域的紋理,如果紋理不是超級大,效果也是很一般:一般高度圖中的兩個像素之間的距離就是對應真實地形中的一米,一米的世界,要用貼圖來表現細節,就是用512*512大小的貼圖,或者是256*256的,那麼,如果用不重複的紋理,實現一個1公裡*1公裡的世界,需要的貼圖大小是256k * 256k,如果效果稍微差一點,就算是一米內只有64個像素,那樣也是64k*64k的紋理。。。所以這樣算下來,使用全域的貼圖的唯一辦法就是用detailed map,而我覺得,這是個醜陋的做法,對於一個以室外情境為主的mmo的網遊來說,地形是給人印象最大的映像元素,而用統一的細節來欺騙效果的做法,給人的印象自然很糟糕。

使用八張不同的紋理

使用8張不同的紋理,進行混合,還需另外兩張不同的調色盤,加上8張normal map,一共加起來是18張紋理,這在某些硬體上面不能實現(好像是只有dx10的硬體才能實現),另外在設定寄存器的時候需要改變相當多的變數。

比較節約的做法就是使用texture atlas,參考nvidia的相應工具(還有代碼和文檔)。將每4張顏色紋理“打包”成一個紋理,這樣,最後就是2大張顏色紋理,兩大張normal map;兩張調色盤紋理,一共是6張紋理,在所有ps2_0以上的硬體上都可以實現。

使用atlas的時候,需要注意紋理座標的改變,原來的紋理座標是[0, 1],應該轉換到[0 +texOffset, 1 – texOffset],其中texOffset= 1 / ( 2*tex_width);

        整合後的diffuse map                                           整合後的normal map

具體實現 pixel shader

texture g_palTex;

texture g_tex0; // Base color texture

sampler tex0 =

sampler_state

{

Texture = < g_tex0 >;

MipFilter = point;

MinFilter = point;

MagFilter = linear;

addressu =wrap;

addressv =wrap;

addressw =wrap;

};

sampler palTex =

sampler_state

{

Texture = < g_palTex >;

MipFilter = linear;

MinFilter = linear;

MagFilter = linear;

};

struct VS_OUTPUT

{

float4 position : POSITION;

float4 texCoord : TEXCOORD0;

float3 diffuse : TEXCOORD1;

float3 normal : TEXCOORD2;

};

float4 RenderWithTexturePS( VS_OUTPUT i ) : COLOR

{

float2 coord0 = i.texCoord.xy * g_fTexRepeat;

coord0 = frac( coord0 );

coord0 = coord0 * 0.5;

float2 coord1 = coord0 + float2( 0.5, 0 );

float2 coord2 = coord0 + float2( 0, 0.5 );

float2 coord3 = coord0 + float2( 0.5, 0.5 );

float4 palColor = tex2D( palTex, i.texCoord.zw ).rgba;

palColor = normalize( palColor );

float4 vDiffuse = tex2D( tex0, coord0 ) * palColor.x +

tex2D( tex0, coord1 ) * palColor.y +

tex2D( tex0, coord2 ) * palColor.z +

tex2D( tex0, coord3 ) * palColor.w;

return vDiffuse * float4( i.diffuse, 1 );

}

以上實現為:一張調色盤紋理,一大張diffuse map(4張不同的紋理整合成的)。頂點中的texCoord.xy為局部的diffuse 紋理的座標,texCoord.zw為全域的調色盤紋理座標。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.