最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

HexMap學(xué)習(xí)筆記(四)——不規(guī)則化

2019-03-04 17:33 作者:皮皮關(guān)做游戲  | 我要投稿

作者:沈琰


前言

這篇教程內(nèi)容主要是對噪聲紋理圖的應(yīng)用,在游戲中噪聲是極為常用的功能,特別是與地形生成相關(guān)的。使用噪聲計算出的地形比較符合自然界的地貌,專欄中還有一篇文章也運用到噪聲,也可以參考閱讀。

傳送門:300行代碼實現(xiàn)Minecraft(我的世界)大地圖生成

本期原文地址:https://catlikecoding.com/unity/tutorials/hex-map/part-4/



這是HexMap系列的第四篇,到目前為止地圖都是一個精確的蜂巢狀網(wǎng)格,這篇教程將會給地圖添加一些不規(guī)則的特性讓其看起來更自然一些。

不再整齊的六邊形

1.噪聲

要添加一些不規(guī)則的感覺就需要一些隨機性,但又不是真的全隨機。我們需要的是在編輯地圖時未選中的位置保持不變,不然每改動一點地圖就全變了。所以需要是一種可重現(xiàn)的偽隨機,即噪聲。

柏林噪聲(Perlin noise)就是一個很好的選項,它在每個點的位置都可以重現(xiàn)。當(dāng)多種不同頻率的柏林噪聲疊加時,能產(chǎn)生大范圍來看變化很大,小范圍內(nèi)又接近一致的噪聲紋理,生成相對平滑的形變,靠近的點更傾向于黏在一起而不是向相反的方向扭曲。

柏林噪聲可以程序化生成,?Noise這篇教程里介紹了該如何去實現(xiàn),但也能使用一張預(yù)先生成的噪聲紋理圖。使用噪聲紋理圖的好處是它比直接計算多頻率疊加的噪聲更容易,也更快,缺點是紋理圖需要占用內(nèi)存并且噪聲的區(qū)域大小相對有限。所以噪聲紋理圖需要平鋪顯示并且需要覆蓋比較大的區(qū)域,使得平鋪看起不那么明顯。

1.1 噪聲紋理貼圖

這里準(zhǔn)備使用噪聲紋理貼圖,所以不必現(xiàn)在去看Noise這篇教程。這表示我們需要一張紋理圖,如同下面這張。

平鋪的不規(guī)則柏林噪聲圖

這張紋理圖包含多重頻率疊加的平鋪柏林噪聲,并且是一張灰度圖,其平均值接近0.5,極限值是0和1。

不過這不是最終要用的圖,這張圖的每個像素點上只有一個值,如果我們需要的是3D形變,我們至少得需要三個偽隨機采樣。所以除此之外還需要兩張額外的不同噪聲紋理圖。

我們可以就用三張不同的紋理圖,或者也可以用RGB通道來存儲這些值,我們可以在一張紋理圖上存儲四種不同樣式的噪聲,如同下面這張圖。

四合一噪聲紋理圖
這張紋理圖怎么獲取的?
用NumberFlow(https://catlikecoding.com/numberflow/)生成的,這是一個我(注:原版教程作者)為Unity寫的紋理編輯插件。(注:自己在工程中使用時直接復(fù)制這張圖就行)

得到這張圖后導(dǎo)入到自己的Unity工程文件中,由于我們要用代碼對紋理圖進(jìn)行采樣,所以它必須是可讀寫的。勾選Read/Write Enable,這樣就能把紋理貼圖的數(shù)據(jù)存儲到內(nèi)存中并用C#代碼進(jìn)行訪問。撤選sRGB(Color Texture)選項,確保格式設(shè)置成Automatic并且壓縮方式設(shè)置成null,我們不想因為壓縮紋理而破壞噪聲圖的樣式。同樣的,Generate Mip Maps也不用勾選,并不需要這個功能。

導(dǎo)入噪聲紋理圖
如果勾選sRGB有什么影響?
如果我們在著色器的某些地方使用噪聲紋理圖,它可能會有些不同。當(dāng)使用線性渲染模式時,紋理采樣數(shù)據(jù)會自動從伽馬空間轉(zhuǎn)換到線性空間。這會對噪聲紋理的采樣數(shù)據(jù)產(chǎn)生一個錯誤的結(jié)果,而這是需要避免的。(注:Unity的默認(rèn)渲染模式是線性模式)

1.2 噪聲采樣

在HexMetrics里添加噪聲采樣方法,這樣就能在任何地方使用,這意味著HexMetrics必須拿到紋理圖的引用。

由于這不是一個組件,我們不能在編輯器里賦值。我們就簡單地把HexGrid當(dāng)一個中轉(zhuǎn),由于HexGrid是第一個運行的,所以在它的Awake方法開始的位置傳遞值就行了。

但是在運行模式下,這種方式無法在重新編譯時保存,靜態(tài)變量不是由Unity序列化的。要解決這個問題還需要在OnEnable方法中重新賦值紋理,這個方法會在重新編譯后調(diào)用。

現(xiàn)在HexMetrics能訪問紋理貼圖了,我們添加一個方便的噪聲采樣方法到里面,這個方法生成一個包含四種噪聲模式的4D向量。

通過雙線性過濾(注:進(jìn)行縮放顯示的時候進(jìn)行紋理平滑的一種紋理過濾方法)的方式對紋理圖進(jìn)行采樣得到樣本數(shù)據(jù),是使用世界坐標(biāo)系下的X和Z軸坐標(biāo)作為UV坐標(biāo)。由于噪聲源是3D的,所以我們忽略的Y軸坐標(biāo)。

最后得到一個可以轉(zhuǎn)換成4D向量的顏色,這個轉(zhuǎn)換是隱式的,這意味著我們可以直接返回顏色,而不用顯式的轉(zhuǎn)換成Vector4。


雙線性過濾是如何工作的?
可以去看Rendering 2, Shader? Fundamentals(https://catlikecoding.com/unity/tutorials/rendering/part-2/)這篇教程,里面介紹了UV坐標(biāo)和紋理過濾。

2.頂點擾動

通過分別擾動每個頂點來讓整齊的網(wǎng)格發(fā)生形變,為此添加一個Peturb方法到HexMesh里負(fù)責(zé)這個工作。這個方法獲取一個點并返回擾動后的坐標(biāo),所以它使用擾動之前點進(jìn)行采樣。

我們先簡單地直接加上X、Y和Z的噪聲采樣,并將其作為結(jié)果。

要如何快速的讓HexMesh里的所有頂點都應(yīng)用擾動?當(dāng)在AddTriangle和AddQuad里添加頂點到列表中時修改每個頂點就行了。

頂點擾動后四邊形依然是平坦的?
很可能并不是。這些四邊形由兩個不再對其的三角形構(gòu)成,因為這些三角形共享兩個頂點,這些頂點的法線會平滑變化。這意味著你看不到兩個三角形之間的明顯過渡,如果扭曲的不是太明顯,你仍然會感覺這個四邊形是平的。
頂點擾動但不明顯

看起來好像沒多大變化,除了單元格的坐標(biāo)標(biāo)簽不見了之外。這是因為我們把頂點加上了噪聲采樣的坐標(biāo),而這些坐標(biāo)總是正值,所以所有三角形都在標(biāo)簽上并覆蓋了它們。我們需要把采樣值的原點放到中心,這樣就能在上下兩個方向上運動,所以修改采樣的范圍到-1至1之間。


單元格中心的擾動


2.1 擾動強度

在HexMesh.Perturb里通過相乘的方式應(yīng)用采樣數(shù)據(jù)。

增強強度


.2噪聲采樣縮放

網(wǎng)格在編輯前還算正常,一旦出現(xiàn)階梯連接部分就不對了。它們的頂點向各個方向扭曲,看起來很混亂,使用柏林噪聲不應(yīng)該會發(fā)生這種情況。

這是因為我們直接用世界坐標(biāo)進(jìn)行噪聲采樣,使得紋理平鋪在每個單元格上,但是我們的單元格的尺寸比紋理貼圖本身要大得多,實際上紋理圖是在任意位置被采樣,這破壞了其連貫性。

10乘10的噪聲紋理網(wǎng)格線覆蓋在六邊形地圖的蜂巢網(wǎng)格上

我們要對噪聲采樣進(jìn)行縮放,這樣紋理就能囊括更大的區(qū)域。我們在HexMetrics里添加這個縮放并設(shè)置其為0.003,然后把采樣坐標(biāo)與這個因素相乘。

3.對平單元格中心

對所有頂點進(jìn)行擾動讓我們的地圖看起來更自然一些了,但是還有一些問題。因為現(xiàn)在單元格的表面不平坦,其坐標(biāo)標(biāo)間與網(wǎng)格相交了,并且在階梯連接部分與陡峭斜面間出現(xiàn)了裂縫。我們先把裂縫的問題放一放,先處理單元格表面的問題。

越不整齊問題越多

相交問題最簡單的解決方法是保持單元格中心平坦,即在HexMesh.Perturb里不修改Y軸坐標(biāo)。

單元格對平

這改動并沒有什么問題,讓識別每個單元格變得更加容易了,并且預(yù)防讓階梯化連接變得混亂的問題。但是垂直方向的擾動依然可以用別的方式做得更好。

3.1 單元格海拔高度的噪聲擾動

我們可以讓擾動作用于每個單元格而不是每個頂點,這樣就既保留了每個單元格的平坦表面又能讓不同單元格之間看起來也有差別。比較好的做法是對高度擾動使用不同的縮放比例,所以在HexMetrics里添加一個強度系數(shù)。1.5的值就能帶來一些微妙的變化,這大概是階梯一級的高度。

修改HexCell.Elevation的set屬性,讓其應(yīng)用垂直坐標(biāo)的擾動。

為了確保擾動能立即被應(yīng)用,需要在HexGrid.CreateCell里精確設(shè)置高度。否則一開始網(wǎng)格就是平坦的。這一步放在UI創(chuàng)建之后的最后一步。

應(yīng)用單元格高度擾動產(chǎn)生了很多裂縫

3.2 使用相同高度

大量裂縫出現(xiàn)在網(wǎng)格中,因為在三角化網(wǎng)格時沒有始終使用相同的高度。添加一個便利的屬性到HexCell里重新獲得自身坐標(biāo),這樣就能在任何位置使用。

現(xiàn)在能在HexMesh.Triangulate里使用這個屬性去確認(rèn)單元格的中心位置。

也可以在確認(rèn)相鄰單元格頂點坐標(biāo)時,在TriangulateConnection里使用這個屬性。

使用一致的單元格高度


4.細(xì)分單元格邊緣

雖然現(xiàn)在單元格有著漂亮的變化,但它們看起來仍然是六邊形。這不是什么大問題,但我們能做得更好一些。

單元格清晰的六邊形結(jié)構(gòu)

如果有更多的頂點自然就能看到更多變化,所以我們把單元格的邊界分為兩個部分,在六邊形的每一片三角形底邊一半的位置添加一個頂點。這意味著HexMesh.Triangulate里需要添加兩個而不是一個三角形。

十二條邊替換六條邊的效果

把頂點個三角形加倍明顯有了些新變化,干脆就把頂點翻三倍,這樣形狀會更堅固一些。

十八條邊


4.1細(xì)分邊緣連接部分

當(dāng)然,還得去細(xì)分連接部分,所以傳遞新的邊界頂點到TriangulateConnection 里。

在TriangulateConnection里添加匹配的參數(shù),就能使用這些新頂點。

還需要從相鄰單元格上計算額外的邊界連接處的頂點,可以在連接到另一邊之后計算。

下一步需要修改邊界的三角化。現(xiàn)在先不管階梯化部分,簡單的添加三個四邊形。

連接處細(xì)分

4.2打包邊緣頂點

因為現(xiàn)在需要四個頂點表示一組邊界頂點,那么把他們打包整合起來就有意義了,因為這比單獨處理四個頂點方便。為此創(chuàng)建一個簡單的結(jié)構(gòu)體EdgeVertices,它需要包含四個順時針排列在單元格邊緣的頂點。

不需要序列化么?
我們只在三角化的時候使用這個結(jié)構(gòu),就此而言我們不需要存儲邊緣頂點,所以不要序列化。

給它一個便利的構(gòu)造函數(shù),只負(fù)責(zé)計算確定的邊緣位置。

現(xiàn)在把三角化的方法進(jìn)行分離,在HexMesh里新建一個從單元格中心到邊緣創(chuàng)建三角形扇面的方法。

以及對兩個邊緣之間的四邊形條帶進(jìn)行三角剖分的方法。

這讓我們能夠簡化Triangulate方法。

我們現(xiàn)在能在TriangulateConnection里使用TriangulateEdgestrip方法,但還有一些其他的細(xì)分工作要做。在我們第一次用v1的地方,我們應(yīng)該用e1.v1代替。以此類推,v2變成e1.v4, v3變成了e2.v1, v4變成了e2.v4。

4.3 階梯細(xì)分

還需要去細(xì)分階梯連接部分,所以把邊緣參數(shù)傳遞到TriangulateEdgeTerraces里。

現(xiàn)在要修改TriangulateEdgeTerraces方法,使之參數(shù)為兩個單元格的邊緣之間,而不是一組頂點。先假設(shè)EdgeVertices里有方便的靜態(tài)插值計算函數(shù),這就可以讓我們簡化TriangulateEdgeTerraces方法而不是使其變得更復(fù)雜。

EdgeVertices方法就是預(yù)先在兩個邊緣之間的所有頂點之間計算階梯插值。

階梯連接處細(xì)分


5.重新連接陡峭面與階梯

到目前為止我們都忽略了當(dāng)陡峭面與階梯相遇時產(chǎn)生的裂縫,現(xiàn)在該處理這個問題了。先處理陡峭-傾斜-傾斜(CSS)和傾斜-陡峭-傾斜(SCS)的情況。

這個問題重現(xiàn)是因為分界點(注:階梯連接收束到陡峭面邊緣的頂點)的計算受到了干擾。這意味著它不是精確的處于陡峭面的邊緣線上,這就產(chǎn)生了一個裂縫,這樣的空洞有的明顯,有的不明顯。

解決方案是不要對分界點應(yīng)用噪聲擾動,就是說我們需要能選擇一個點是否應(yīng)用擾動。最簡單的辦法是創(chuàng)建一個完全不對頂點進(jìn)行擾動的AddTriangle方法。

修改一下TriangulateBoundaryTriangle讓其應(yīng)用這個方法,這意味著只對分界點除外的其他所有頂點進(jìn)行擾動。

值得注意的是,因為我們沒有用v2推導(dǎo)其他的點,它可以直接先應(yīng)用擾動。這是一個簡單的優(yōu)化辦法并且節(jié)省代碼,所以我們改成如下這樣。

未應(yīng)用擾動的邊界點

現(xiàn)在看起來好多了,但是還沒完。在TriangulateCornerTerracesCliff方法里,分界點是通過插值計算左右的坐標(biāo)點得到的,但這兩個點沒有應(yīng)用擾動。要讓邊界點能精確吻合陡峭斜坡邊緣,需要插值去計算兩個擾動過的坐標(biāo)點來求得邊界點。

這在TriangulateCornerCliffTerraces方法里也是一樣的。

不再有空洞了


5.1兩個陡峭斜面與一個傾斜面的情況

剩余的是有兩個陡峭斜面與一個傾斜面特征的情況。

因為一個三角形頂點位置產(chǎn)生的空洞

這個問題的修正方法是在TriangulateCornerTerracessCliff方法最后一個else里,單獨擾動除了分界點外的三角形頂點。

TriangulateCornerCliffTerraces里也一樣。

最后一個空洞也消失了


6.優(yōu)化調(diào)整

我們現(xiàn)在有了一個準(zhǔn)確的應(yīng)用擾動的網(wǎng)格,它的具體形狀表現(xiàn)取決于一張?zhí)囟ǖ脑肼晥D,它的縮放和擾動強度。在我們現(xiàn)在的例子里它的擾動強度似乎過大了,雖然這對表現(xiàn)單元格的不規(guī)則化很不錯,但我們還是不想它偏離網(wǎng)格太多。畢竟最終我們還是要通過它去識別哪個單元格正在被編輯,如果變化太大就很難去填充編輯一些別的什么東西。

頂點擾動與未擾動的對比

單元格的擾動強度設(shè)置為5有點太大了。

讓我們降低到4,讓它變得更容易管理而又不會太有規(guī)律,保證在XZ平面的最大位移為√32 ≈ 5.66。

強度為4的的擾動

另一個可以調(diào)整的參數(shù)是內(nèi)部固定六邊形的范圍。如果我們增加它,平坦的單元格中心就會變得更大一些。這給未來的內(nèi)容留下了更多空間。當(dāng)然,也也會變得更六邊形化一些。

少量增加固定六邊形范圍參數(shù)到0.8會讓之后使用起來更容易。

固定六邊形的范圍為0.8

最后,海拔高度等級之間的差異有點陡峭。當(dāng)我們檢查網(wǎng)格生成是否正確時這很方便,但這一步我們現(xiàn)在已經(jīng)完成了,所以讓我們把它減少到3。

高度等級的步長減少為3

還可以調(diào)整高度擾動強度。但它現(xiàn)在被設(shè)為1.5,等于高度步長的一半,這已經(jīng)比較合適了。

較小的高度步長也使得使用我們可以更為實用的7個高度等級,這允許為地圖添加更多種類。

使用了七個高度等級的地圖

下一篇是:?https://catlikecoding.com/unity/tutorials/hex-map/part-5/

本期工程地址:https://github.com/tank1018702/Hex-Map-Learning/tree/Irregulatity


有想系統(tǒng)學(xué)習(xí)游戲開發(fā)的童鞋,歡迎訪問http://levelpp.com/?

有專業(yè)開發(fā)交(gao)流(ji)群等待大家強勢插入:869551769

HexMap學(xué)習(xí)筆記(四)——不規(guī)則化的評論 (共 條)

分享到微博請遵守國家法律
射洪县| 岑巩县| 资溪县| 涟水县| 唐山市| 鹤山市| 酉阳| 惠来县| 尉犁县| 延寿县| 修武县| 紫阳县| 周宁县| 东平县| 确山县| 利辛县| 明水县| 孟村| 清涧县| 青铜峡市| 彩票| 宁强县| 南华县| 屏东市| 孟连| 富川| 新民市| 星子县| 东丰县| 鄱阳县| 广元市| 宜黄县| 壶关县| 洮南市| 鹤岗市| 抚松县| 甘肃省| 赤壁市| 济南市| 嵊州市| 宿迁市|