用Atlas實現多張貼圖混合的地形及注意事項
對於面積很大的地形,4張diffuse圖就使得表現受到局限,顯得單一了。解決方法有2:
1,切分地形為多個小塊,但要能同時編輯多塊地形,處理地形邊界。
2,讓一個地形能混合更多的貼圖。
本文是關於第二種方法的,並且不增加drawcall,shader計算量也沒有太多增加。
思路是把所有要混合的圖做成一張Atals,比如最上面的貼圖,4x4的能有16種。但權重圖仍然只有一張RGBA的話,就得單個通道能區分開4張圖才行。RGBA通道分別表示4x4的一列,R的取值是0-255(color32算的話),可以分成4段:0-63,64-127,128-191,192-255分別表示一列中的某一行,某一段內的取值表示權重,0是0%,63是100%,精度降低到原來的1/4。GBA通道同理。刷地形的工具保證任一點的所有權重加起來是63。
算格子和格子權重的大致代碼如下(shader里顏色是0-1)
tntfloat4 splat_control = tex2D(_Control, IN.tc_Control); //權重圖ntsplat_control = min(splat_control, fixed4(0.999, 0.999, 0.999, 0.999));tt//防止1的出現nthalf4 gridIndex = floor(splat_control * 4);tt//每一個通道表示那一列中從下到上第幾個,[0,3]。nthalf4 gridWeight = floor(splat_control * 256) - gridIndex * 64;tt//每個格子里的權重值,[0,63]。n
把uv還原到一個格子里的演算法
uv0 = frac(IN.uv_Splat0) * float2(0.25, 0.25) + float2(0, gridIndex.r * 0.25);n
下面是效果示例,帶發現光照的。

注意事項:
1,diffuse atlas的mipmap

解決方法是傳入額外的參數調用tex2D告訴它實際的uv插值,ddx和ddy用來做這個
tex2D(_Splat0, uv0, ddx(IN.uv_Splat0), ddy(IN.uv_Splat0))n
函數解釋詳見下面的參考。所有atlas帶mipmap需要平鋪的都要解決這個問題。
2,權重圖的filter mode

Bilinear適合算格子的權重,過度自然,但是在邊緣可能採樣出超過本格子範圍的值,比如64-127的格子,可以採樣出小於63或大於127的值。所以用一個中間方案,一部分用point一部分用bilinear,point採樣值小於某個格內權重閾值時(比如6)用point的,超過的話用bilinear的,這樣儘管大於6才開始過度,但是過渡是正確的。
unity里的terrain貼圖是它引擎設置的,要想sample2次可以把權重圖copy一份,但是這就有內存代價了,這個圖是不壓縮的,1024x1024的RGBA要多佔5M都內存吧,算上mipmap。

編輯器代碼是在unityEditor寫的,c#的,可以反編譯出來,仿照著實現一邊是可以的就是代碼多了些。另一種方式是繼承terrainInspector至改寫特定的方法,這樣代碼量就少很多了,把OnSceneGUI里刷貼圖的部分改一下支持刷16格工作量並不大。
參考資料
1,《天下》手游,它的地形是用4x4的atlas,但更複雜還能混合出帶反射的水,並且一個通道表示0-15的格子。細看能看出來有的過渡邊緣比較糙,估計跟點採樣有關。
2,http://hacksoflife.blogspot.tw/2011/01/derivatives-i-discontinuities-and.html
解釋了如何計算使用哪層mipmap的原理
3,cg library里tex2d、ddx、ddy
tex2D
推薦閱讀:
※為了忘卻的紀念:析OpenGL史上第二偉大的擴展,DSA
※OpenGL+FreeType 模仿黑客帝國數碼雨 V2.0
※卡通渲染及其相關技術
※神奇的深度圖:複雜的效果,不複雜的原理
