Import RealWorld Landscape to UE4

簡介

從真實世界地形資料匯入遊戲引擎是蠻常見的需求,
這次剛好有機會在UE4試試看這個功能,
沒想到遇到地形交界處接不起來的問題,
最後找到問題點,所以順便分享作法。

本篇文章使用的流程。
不過過程中有遇到問題,所以寫了這篇文章想要補充不足之處。

以下是簡化版的流程:
下載地形資料並轉換為引擎可匯入的heightmap png檔。
1. 到https://viewer.nationalmap.gov/basic/下載地形資料
2. 到https://github.com/MacroPolo/real-terrain下載python script
3. 解壓縮地形資料檔,放到real-terrain的input資料夾內
4. 如果電腦沒安裝python,安裝python
5. 在命令列輸入python real-terrain.py [檔案名稱].img (註1.)
6. real-terrain的output資料夾內就會有輸出好的png檔。

在UE4匯入地形檔:
1. 創一個新地圖,WorldSettings設定Enable World Composition為true
2. Levels->Create New,產生新的子地圖
3. Models->Landscape->Manage->New Landscape->ImportFromFile
4. Heightmap File選擇前一個步驟6輸出好的png檔
5. 地形匯入完成。
6. 在World Composition視窗調整地圖到想要的位置。

以上是基本流程,不包含太多的細節,一些介面是從哪邊叫出來的就請參考影片。
在我的實驗中,用上面的流程匯入一塊地形檔是不會有任何問題的。

但是當我匯入兩塊地形檔放進UE4後,發現兩塊地形接不上,有嚴重的高低差問題。

這是本系列的第一篇
目前一共有以下幾篇為系列的文章

1. Import RealWorld Landscape to UE4

2. 了解Landscape的組成元件

3. Generate Navigation Mesh with World Composition

4. Build Static Navigation Mesh in World Composition

修正地形接縫問題

經過一番追查,最後我找到幾個項目要特別處理,才能把地形接起來。
1. 最大高度要一致。
2. height map解析度要符合引擎的規範。

最大高度要一致


以我的範例來說,我有兩張地圖檔49_361跟49_362。
在使用real-terrain轉換的過程,會顯示這個地形檔的最小高度與最大高度
舉例來說

這是49_361的結果
>>> Extracting elevation information from input DEM.
Minimum elevation: 0m
Maximum elevation: 180m
Elevation range: 180m

這是49_362的結果
>>> Extracting elevation information from input DEM.
Minimum elevation: 0m
Maximum elevation: 154m
Elevation range: 154m

real-terrain會將地形最小高度與最大高度normalize為uint16,值域是0到65535,
所以兩張地形分開處理的話,
49_361轉出來的最大值是180m高,49_362轉出來的最大值只有154m,但是值都是65535。

為了解決這個問題,代表在匯入地形的時候,需要找到所有匯入的地形檔的最大高度與最低高度。
然後用整體最大/最低高度作為基準參數,提供所有地形檔轉換。
以上面的範例,最低高度就是0m,最大高度就是180m。

強制所有地形檔以預先算好的最高/最低值做轉換


這部份要修改一下python程式碼
使用任何文字編輯器開啟real-terrain.py
找到_find_elevation_range(self):內
self.min_elevation = blabla
self.max_elevation = blabla
改為
self.min_elevation = '0'
self.max_elevation =  '180'

然後重新用real-terrain.py轉換地形檔就可以得到相同值域的height map。

Height map解析度要符合引擎的規範。


做了上面的步驟,我以為地形要能接上了,結果如下圖1與圖2,
依然沒有接上,觀察了一陣子覺得應該不是高低差問題,而是整個接縫處本來就不連續。
但是原始height map的png檔又是接得起來的(用繪圖軟體將拼起來看)。
所以初步把問題排除是real-terrain產生的圖有問題,將問題範圍限縮到UE4。

圖1. Gap between landscapes without normalize height. (Top view)

圖2. Gap between landscapes without normalize height. (Side view)



後來在搜尋的過程中找到這個頁面
https://docs.unrealengine.com/en-US/Engine/Landscape/TechnicalGuide/index.html#recommendedlandscapesizes

裡面有描述到UE4對height map的圖片是有規範的。
而我從real-terrain輸出的原圖大小是10012*10012,明顯比UE4最大的8129*8129還要大。
所幸real-terrain也支援圖片的縮放,
所以只要將執行指令改為
python real-terrain.py -s 8129 [檔案名稱].img
就可以了。

在輸出的output資料夾就會多一個_scaled.png檔。
使用這個png檔在UE4匯入後,接縫處終於正常了。如圖3.與圖4.

圖3. Almost no gap after adjust. (Top view)

圖4. Almost no gap after adjust. (Side view)


使用real-terrain輸出多個tile map。


我本來的問題其實在前面就結束了,不過有覺得地形有點太大張想要細切。
又看到UE4好像有另一個可以import tiled map的介面,
於是繼續試試看能不能把兩個大地形檔再細切。
這部份其實還是著重在延伸real-terrain的程式。

利用real-terrain輸出tile map


首先先學習如何利用real-terrain輸出tile maps。
一樣要注意UE4的height map圖片規範,這裡就以每個tile是2017*2017為範例。
python real-terrain.py -t 2017 [檔案名稱].img
基本上這個指令就會把地形檔以2017*2017為單位進行分割。
但是可惜的是,原圖10012以2017是沒辦法整除的。最後會留下1944*2017的非正方形圖。
所以我們要先把原圖縮放到2017的倍數,再進行分割。

先縮放再分割


這部份也需要修改python程式碼,因為real-terrain在分割的時候只吃原始圖檔。
現在我們需要先縮放,再對縮放的結果分割。

找到函式_generate_tile(self)的段落,裡面的
img = Image.open(self.output_full)
改成
img = Image.open(self.output_scale)

然後在命令列輸入
python real-terrain.py -s 8068 -t 2017 [檔案名稱].img
就能產生出各個tiled maps。

到UE4的Levels->Import Tiled Landscape選擇剛才所有輸出的tiled png檔。
如圖所示
圖5. Import tiled Landscape from Levels.


會發現UE4除了圖片規範之外,批次匯入tile maps還有命名規則。
如圖所示,命名規則是[檔案名稱]_X[數字]_Y[數字]

圖6. Name rule when import tiled landscape.


符合Import TileMap命名規範


所以回到real-terrain.py,
找到函式_generate_tile(self)的段落,把裡面的
img_slice.save(self.output_tile + "_heightmap_tile_{}-{}.png".format(x, y))
改成
img_slice.save(self.output_tile + "_heightmap_tile_X{}_Y{}.png".format(x, y))
輸出來的圖片應該就是正確的了。


以上就是整個從真實地形檔匯入UE4所遇到的各個問題與解決過程。
簡單列一下重點就是:
1. png檔大小要符合UE4規範
2. 所有地形檔在匯出png的時候要用統一的最大高度/最小高度。
3. 為了達到這個目的,修改real-terrain是必要的。
4. 簡單的方法就是算好最大/最小高度後硬寫在程式碼內,
5. 如果需要更正式的製程的話可能就要修改更多程式碼。
6. 或是使用付費的工具World Machine、World Creator來作。
7. 如果要輸出tiled maps,要修改tile的輸入是scaled的image,也要修改輸出的命名規則。


註1.
如果跳以下錯誤訊息
ModuleNotFoundError: No module named 'PIL'
可以輸入pip install Pillow 安裝
如果還是跳錯
輸入Python pip install Pillow

參考資料


本篇文章使用的流程。
不過過程中有遇到問題,所以寫了這篇文章想要補充不足之處。

USGS的下載處,提供多種精細度的資料下載。

將USGS的地形資料轉換成16 bit PNG heightmap的python程式
支援IMG, ArcGrid以及GeoTIFF檔
也支援圖檔的scale,或是將整個大地圖細切為多個tile。
python程式本身也寫得簡單易懂。
用這支程式可以取代GlobalMapper的部分功能。

有非常詳細而且完整的產生真實世界地形的圖文說明
裡面的範例還包含地形材質的匯入,鐵道,道路、河流等等的匯入
唯一的可惜之處就是使用了幾個付費軟體
包含GlobalMapper (處理地形資料)與
UE4的plugin Procedural Landscape Ecosystem
鐵道、道路、河流等Vector的匯入仰賴GlobalMapper之外,
也要用該Blog自己寫的plugin匯入進UE4
但是這個Plugin好像只維護到4.19

Unreal對landscape的基本說明。
裡面最重要的就是Recommended Landscape Sizes了,不管是要用Import TiledLandscape還是直接Import landscape。
height map都要照這裡面的規範製作。否則會錯誤或是直接無法匯入。

使用OpenTopography(地形資料)加上L3DT(heightmap處理)匯入真實地形到UE4的影片教學
本篇文章沒有使用到這個流程,不過要拿US以外的地形資料可能要參考這個流程。



留言

這個網誌中的熱門文章

UE4 除錯技巧分享 (一)

UE4 GameplayAbilityTask介紹

UE4 Navigation Mesh 心得