Houdini技術體系 基礎管線(三) :UE4以選擇區域的方式對地形做生成和更新 上篇
阿新 • • 發佈:2018-06-09
class 通過 例如 tex 總結 對比 RM span 裏的 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內部修改還是會有以下幾個問題:
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的數據做更新。
背景
前一節裏,解決了Houdini地形無縫導入到UE4的流程問題。但這種方法也有它的局限性,在實際遊戲項目裏,LA和LD還是偏向在遊戲引擎編輯器裏工作,他們的一些設計也會影響到地形的信息,那麽就需要Houdini對已經導入UE4中並Bake成Landscape的地形資源做二次修改。通常會選擇兩種方案:- 方案一:把整個地形和建築都導回到Houdini裏,重新過程化和調整生成後,再全部導入回UE4做處理。
- 方案二:使用HDA節點的Input和Output,通過調用Houdini Engine API,直接在UE4裏完成調用Houdini過程化節點對地形做修改。
- 雖然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裏做資源的版本管理,也不方便多人合作地形
使用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 float3. 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就可以了。// Set Layer Data for (int32 LayerIndex = 0; LayerIndex < ImportLayerInfos.Num(); LayerIndex++) { LandscapeEdit.SetAlphaData(ImportLayerInfos[LayerIndex].LayerInfo, SelectLandscapeComponent->GetSectionBase().X, SelectLandscapeComponent->GetSectionBase().Y, SelectLandscapeComponent->GetSectionBase().X + SelectLandscapeComponent->ComponentSizeQuads, SelectLandscapeComponent->GetSectionBase().Y + SelectLandscapeComponent->ComponentSizeQuads, (uint8*)ImportLayerInfos[LayerIndex].LayerData.GetData(), 0); }
然後選中一個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。ULandscapeInfo* LandscapeInfo = SelectLandscapeComponent->GetLandscapeProxy()->GetLandscapeInfo(); FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); int Num = IntHeightData.Num(); for (int i = 0; i < Num; i++) { // Convert Transform IntHeightData[i] = (IntHeightData[i] - ZeroValueInDigit)* SelectLandscapeScale.Z / OldLandscapeScale.Z + 32768.f; } // Set HeightData LandscapeEdit.SetHeightData(SelectLandscapeComponent->GetSectionBase().X, SelectLandscapeComponent->GetSectionBase().Y, SelectLandscapeComponent->GetSectionBase().X + SelectLandscapeComponent->ComponentSizeQuads , SelectLandscapeComponent->GetSectionBase().Y + SelectLandscapeComponent->ComponentSizeQuads , (uint16*)IntHeightData.GetData(), 0, false );
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邊界問題。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">
Houdini技術體系 基礎管線(三) :UE4以選擇區域的方式對地形做生成和更新 上篇