[UE4]以選擇區域的方式對地形做生成和更新
前一節裡,解決了Houdini地形無縫匯入到UE4的流程問題。但這種方法也有它的侷限性,在實際遊戲專案裡,LA和LD還是偏向在遊戲引擎編輯器裡工作,他們的一些設計也會影響到地形的資訊,那麼就需要Houdini對已經匯入UE4中並Bake成Landscape的地形資源做二次修改。通常會選擇兩種方案:
- 方案一:把整個地形和建築都導回到Houdini裡,重新過程化和調整生成後,再全部匯入回UE4做處理。
- 方案二:使用HDA節點的Input和Output,通過呼叫Houdini Engine API,直接在UE4裡完成呼叫Houdini過程化節點對地形做修改。
這裡方案一不但要求美術和策劃對Houdini有一定了解,而且因為Houdini裡和引擎的渲染效果不一致。可能還需要匯入到UE4裡才能看到最終效果,大地形還要做WorldCompositon和LandscapeStreamingProxy的生成。除了地形以外的的場景和建築部分,還會和GamePlay以及優化顯示邏輯相關,通常會包裝成BP或Prefab的形式,這些東西要匯入Houdini再導回也不僅僅是資源處理的工作。這麼看方法一是非常費時費力的方法。這也導致了國內一部分專案雖然是把Houdini的地形引入到製作管線裡,但也僅僅是作為WordMachined的替代品,並不能完全發揮Houdini的全部功能。
所以,我們的目標還是方案二的開發方式,除了第一次在Houdini裡做完初始地形匯入到UE4裡生成WorldCompositon後,就不再需要重新導回到Houdini,而是在UE4裡呼叫預先封裝好過程化功能的HDA節點來完成功能。這也是近年來Ubisoft在Tom Clancy’s Ghost Recon: Wildlands和Far Cry 5裡使用Houdini的方案。具體案例在GDCVault上有GDC2017和GDC2018的相關視訊,這裡的目標也是要UE4裡用類似他們的方法來實現功能。
圖:Far Cry 5的地形編輯工具示例。可以直接在編輯器裡呼叫Houdini功能對地形做修改。
但是使用原生的UE4 Houdini Engine的前提下,無縫大地型的UE4內部修改還是會有以下幾個問題:
- 雖然Houdini Engine支援Landscape的讀回到Houdini,但他Output只支援使用回讀的Landacape Data資訊建立一個新的Landscape:
- 每次修改,都要重新跑一遍上節提到的生成WorldCompositon和LandscapeStreamingProxy的過程,重新對地形對切割,還是非常耽誤時間
- 雖然提供了基於Landscape Component的Input,但Output時每個Component會Cook成一個landscape,導致生成多個Component。
- 在原生配置下,即便只處理一部分地形的迭代也必須把整個Landsacape Data通過HDA Input到Houdini來進行處理:
- 假如使用Houdini裡讀入8x8k的地形,記憶體佔用和過程化處理和交換資料量都會變大,從而導致Cook時間變長,降低迭代的速度。
- 不能基於Componment的控制生成範圍,那就需要給HDA額外加入一個選擇區域的Input,導致美術工作上變的更加繁瑣。
- 很難在UE裡做資源的版本管理,也不方便多人合作地形
對前面提到的一些概念和方法不瞭解也沒關係,我接下來會用示例還原這些過程,直到最終的目標方案的原型,也就是Far Cry 5的功能效果。
圖 Far Cry 5 地形編輯器,可以選擇一個Terrain的Section或Sector區域來做過程化生成,極大的減小了處理和引擎與Houdini交換的資源量。
時間和篇幅的緣故,本節會分為上下篇,上半部分主要是在如何修改Houdini Engine原始碼,可以在UE4裡基於Landscape Component進行小規模的迭代迭代的功能,下篇則是實現Houdini Engine管線的基礎上,如何製作HDA節點,實現各種不同的編輯效果。
使用Houdini製作閉環
本節繼續使用上一節的場景資源做作為測試用例,講解一些HDA Input基礎知識,讓沒有Houdini開發經驗的程式人員也能很快接手Houdini Engine的改造。首先建立一個用來開發除錯用的HDA節點,它的功能就是可以選中UE4場景裡的一個Landscape作為Input,通過Houdini Engine把UE4 Landscape Height Data和Layer Data 轉換為Heightfiled Height和Mask Data傳入到HDA,在HDA裡不做任何處理直接輸出原始的Height和Mask,再次經過Houdini Engine生成UE4的Landscape Data。
下圖就是一個Houdini閉環處理地形的流程展示,不需要開啟Houdini,在UE4裡就能完成閉環的操作。
如何建立一個SOP(Surface OPerators or geometry nodes )型別的Houdini Node Input的流程,Houdini Engine的官方文件裡也有講解 https://www.sidefx.com/docs/unreal/_inputs.html
方法一,Houdini裡File->New Asset,按下圖的建立一個新的Operator
方法二 建立一個Gemotry節點,Create Digital Asset,在HDA的Parameter裡新增一個Operate Path的引數,把這個引數的與Object_Merge的Object做關聯。
不論用這兩種哪個方法制作都可以得到這個HDA檔案,把它加入到UE資源並拖入到Level的話,按下圖那樣把Input型別選擇為Landscape Input,就可以選擇要處理LandscapeStreamingProxy。而勾選上最下面的“Export Selected Landscape Components Only”,就可以把Landscape Component作為Input輸入給Houdini。但就像一開始提到的,原生Houdini Engine的這個功能並不能滿足我們的需求。
基於component的更新的問題
如下圖所示,原生UE4的Landscape的是支援多選Component Selection的,Houdini Engine也是支援多個Landscape Component 的Input。
選擇4x4個Component作為要處理的Landscape資訊,然後用Recommit看下結果.
如下圖所示,雖然在效果面上,Houdini Enine把讀入的LandScape Data轉成HeightField Data 輸入給Houdini又沒有絲毫誤差的的Output後轉為LandScape Data,但Houdini Engine把這16個Component建立成了16個Landscape,這明顯不是想要的結果。
另外要注意的是,新建立的Landscape的Transform和老的Landscape的Transform也不一樣,這是Houdini和UE4的高度資訊單位不同導致,這個問題也會在後面修改Houdini Engine時造成一定的困擾。而且原生的Component多選功能在Height和Mask更新上也會有一些問題。另外預先提到的一點,雖然Houdini Engine的Landscape的更新上有以上各種問題,但類似讀取Landscape的資訊來動態擺放,生成植物生態系統等不會修改LandscapeData的功能並不會受影響,這個具體的HDA開發也會在下篇裡涉及到。
圖:類似FarCry5根據選擇地塊的Mask資訊生成植被的管線,原生的Houdini Engine也是可以勝任的。只需要一些Houdini HDA的功能開發就可以了。
定製Houdini Engine支援基於Component的生成和更新
因為時間和篇幅關係,這裡提供一個不需要修改UE4原始碼,只少量修改Houdini Engine就可以解決問題的方法,先進入到UE4的Houdini Engine Plugin工程程式碼裡。
和Landscape相關的功能,是在HoudiniLandscapeUtils和HoudiniEngineUtils裡,建議有時間還是全看一遍,這裡用註釋簡單描述下整個資料流程方便定位問題。
Input部分:
呼叫FHoudiniLandscapeUtils::CreateHeightfieldFromLandscapeComponentArray函式,把選擇的Landscape Component的Height Data資訊轉為Houdini的HeightField Data。
1. Extracting the height data
2. Convert the height uint16 data to float
3. Set the HeightfieldData in Houdini
4. Extract and convert all the layers
// 1. Extract the uint8 values from the layer
// 2. Convert unreal uint8 to float
// 3. Set the heighfield data in Houdini
Output部分:
當資料在Houdini裡處理完成後,呼叫FHoudiniLandscapeUtils::CreateAllLandscapes基於Houdini的volume資料生成Landscape。
// First, we need to extract proper height data from FoundVolumes
// Check that all layers/mask have not changed too
// Extract the Float Data from the Heightfield
// Convert the height data from Houdini's heightfield to Unreal's Landscape
// Look for all the layers/masks corresponding to the current heightfield
// Extract and convert the Landscape layers
// Create the actual Landscape
定位到FHoudiniLandscapeUtils::CreateLandscape函式裡,它的核心功能把轉換後的的uint16的Landsacpe的Height資訊(TArray< uint16 >& IntHeightData)和Layer資訊(TArray< FLandscapeImportLayerInfo >& ImportLayerInfos),通過呼叫UE4的ALandscapeProxy::Import來生成一個全新的Landscape。
這裡選擇的解決方案是不建立新的Landscape,把HeightData和LayerData做適當的封裝,直接使用FLandscapeEditDataInterface的SetHeightData和SetAlphaData輸入到需要修改的Landscape的對應Component的資料做更新。
Layer Data的處理方法
為了講解簡單起見,直接在CreateAllLandscapes函式的後面加上這部分功能。其中的Height Layer的更新相對簡單,把之前Import用的TArray< FLandscapeImportLayerInfo > ImportLayerInfos的資料對應的用LandscapeEdit.SetAlphaData傳給老的Landscape就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
然後選中一個Component,給HDA配置上Landscape材質,以及一個HeightField Mask Noise節點,給地形的Groud資訊圖層資訊增加一些噪聲 。
左邊是處理前的效果,右邊是增加噪聲後的效果。
可以看到,這裡已經把Houdini處理過的Height Mask資訊寫回到了UE4原本的Landscape Layer上,實現了目標的效果。
Height Data的處理辦法
和處理Layer Data的方法類似,把Houdini Engine Output的(TArray< uint16 >& IntHeightData)用LandscapeEdit.SetHeightData函式傳回給Landscape Component。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Convert Transform註釋部分的處理,是因為經過重新處理後Houdini的Height Field的Data Range和之前的發生了變化,導致ConvertHeightfieldDataToLandscapeData裡生成的HeightData和Transform資訊和Input時的Transform資訊不匹配,ZeroValueInDigit和OldLandscapeScale.Z分別代表了新地形的Transform資訊。需要把兩個Transform資訊的差異對HeigtData做修正,才能把正確的Height Data寫回到Landscape。如果你發現寫回的地形整體高了一塊或低了一塊,或者高度比例和原來不一致,那通常就是這個HeightData的還原處理出錯了。所以原始的Landscape Transform也要儘量標準,例如本節示例裡初始Landscape的Transform就做的儘量正規化
看下修改程式碼後的效果,選擇一個Landscape Component地塊,在HDA節點裡增加一個Heightfiled Noise的節點,對Landscape Height Data做一些輕微的噪聲修改:
左側是未處理的,右側是處理完的。可以看到地塊有了噪聲的高低差的效果。
繼續做一個Height Field Erode的測試,用TimeShift來控制Height Field Erode的演算幀數。
下圖結果是TimeShift = 30 和 TimeShift = 60的效果對比。基本上實現了用Houdini Engine對一個Landsape Component的修改功能。但是問題也很明顯。處理的Component和未處理的Component的邊緣高度無法很好的銜接。距離推上生產線,還有不少功能需要開發和支援。
圖:Height Field Erode效果生成,相比整個Landscape的演算,一個Component只要幾秒內就能完成效果計算。
總結
Houdini Engine基於Landscape Component的過程化生成,確實可以大幅度的提升生成效率和速度,但是Houdini Engine和HDA製作都還需要一系列的定製開發
- 對多選Landscape Component的支援,並且解決多個Component之間的接縫問題。
- Input不能只有Landscape Component來控制範圍,還需要為美術提供選區的功能來控制生成範圍,避免Component邊界問題。
在下篇中,我們會針對這些問題,繼續對Houdini Engine進行定製,以及提供針對不同功能的HDA開發的示例。