1. 程式人生 > >Houdini技術體系 基礎管線(三) :UE4 Landscape Component的多選支持 下篇

Houdini技術體系 基礎管線(三) :UE4 Landscape Component的多選支持 下篇

基本上 idt python obj als arr 合並 bsp c++

背景

上篇中,我們介紹了如何修改Houdini Enigne來設置單個Landscape Compnent的Height和Layer的數據,但原生Houdini Engine並不支持多選Component的寫回功能,下篇中,我們來解決這個問題。

Component多選支持的修改

Houdini Engine雖然支持多個Landscape Component的選擇,但是並不支持寫回到Landscape Component,需要自己來實現這個功能。單個Component的實現方法上文已經接受。 多選和單選區別只是在要把所選的Landscape Component按提交給Houdini的順序來保存。
通過閱讀Houdini Engine代碼可以看到FHoudiniLandscapeUtils::CreateHeightfieldFromLandscapeComponentArray的參數LandscapeComponentArray裏有所有提交的Landscape Component,不過LandscapeComponentArray中保存順序並不是真正的提交順序,而Houdini Engine Output出來的處理結果的順序是Input的順序的是一致的,如果直接用LandscapeComponentArray的結果,就會導致Component的不對應,所以這裏我是在FHoudiniLandscapeUtils::CreateHeightfieldFromLandscapeComponent函數裏把Input的Component保存起來,也保證了保存順序。
技術分享圖片 然後在FHoudiniLandscapeUtils::CreateAllLandscapes函數中,就可以把每個FoundHeightfield和LandscapeComponent做對應,來調用LandscapeEdit.SetHeightData來更新這個Component的Height Data了。
  for ( TArray< const FHoudiniGeoPartObject* >::TConstIterator IterHeighfields
    ( FoundHeightfields ); IterHeighfields; ++IterHeighfields )
  {
      SelectLandscapeComponent = 
      SelectLandscapeComponentArray[ComponentIndex];

而Layer Data的保存方式和Height Data,所有Landscape Component的Layer都保存在一個ImportLayerInfos,比如選了4個Component,每個Component有4個Layer,ImportLayerInfos裏就有4x4 16個Layer Data的信息,這裏也需要自己按Component和每個Component的Layer數量來提交。下面偽代碼,LayerNum為每個Component的Layer的數量,ComponentIndex為處理的Component的編號,而實際開發情況下,可能每個Component的Layer的數量和命名都不一樣,那就需要根據規則來定制這裏的算法了。
 
// Set Current Component‘s Layer Data
for (int32 LayerIndex = LayerNum * ComponentIndex; 
LayerIndex < LayerNum * (ComponentIndex + 1); LayerIndex++)
{
    LandscapeEdit.SetAlphaData
}
ComponentIndex++;

  

這樣修改後,Houdini Engine就可以支持多選Landscape Component的Input和Output了。這裏使用上節用到HDA文件,選中4個Component做HeightField Noise的生成 技術分享圖片 技術分享圖片 但結果跟我們預想的並不一樣,而且只有第一個Component被做了Noise處理。。。 技術分享圖片 這是UE4原生的Houdini Engine的Input的數據和我們的HDA的處理算法不匹配導致的。

修改HDA對Input的支持

造成這個結果的原因,要從Houdini Engine生成Input的函數FHoudiniEngineUtils::HapiCreateInputNodeForLandscape入手:
// 1. Create the heightfield input node.
// We‘ll use its mergeId to connect all the landscape layers,
// while it‘s displayId will be our connected asset ID
FString LandscapeName = LandscapeProxy->GetName() + TEXT("_Merge");
HAPI_NodeId MergeId = -1;
if ( !FHoudiniLandscapeUtils::CreateHeightfieldInputNode( ConnectedAssetId, MergeId, LandscapeName ) )
    return false; 
  這裏是通過Houdini Engine,直接創建了一個Houdini的Merge節點,然後把每個Component的Landscape Height Data和Layer Data轉為HeightField的Height和Mask Volume,在Merge到一起,也就是用C++代碼來生成HDA節點,這樣就保證了所有Input的整體處理,而且也可以程序化的去對應不同的Input情況,在後面的章節裏,很多Input項目也是要使用C++或Python來生成HDA節點來節省開發成本。下圖就是程序生成HDA節點的效果示意: 技術分享圖片 再增加一個Heightfield noise 看下效果:和之前閉環測試效果一樣,因為默認的Houdini HeightField節點並不支持這種多個Volume Merge的處理。 技術分享圖片 這裏介紹三種解決方法: 方法一是直接修改或重寫HeightField Noise節點來支持整個Merged Volume: 技術分享圖片 方法二:用Loop處理每個Height Volume 技術分享圖片 方法三:用tilesplice把Volume合並到一起做處理,然後再用split重新切開 技術分享圖片 如果不想自己修改或定制節點的話,方法二和三都可以,感覺方法三還更省事,但方法三有以下幾個問題: 一個是Tile順序的問題。UE4裏的Tile和Houdini的Tile的行列是不同的,同樣一個2x2的Compnent,他的Input和Output的順序有所不同: Input Output 1 2 1 3 3 4 2 4 這個需要自己開發功能調整,另外,方法三所選的Component也必須是NxM這種連續的Volume,否則tilesplice節點會像下圖這樣幫你補齊。所以方法三也有不少的限制。 技術分享圖片 如果時間允許,還是自己開發一套Heightfield的節點來定制需要的功能,原生的HeightField系列節點在內存上也有些浪費,限制也比較多,而方法二和三可以作為臨時應急方法。

邊緣法線問題處理

再運行一次HDA處理,2x2的4個Component的節點確實都做了處理,但是在Component邊緣有很明顯的接縫問題,在World Normal視圖下更明顯。 技術分享圖片 技術分享圖片 造成法線接縫是Component之間是共享邊緣造成的,單個Landscape Component的LandscapeEdit.SetHeightData即便選擇計算法線,也會導致邊緣因為采樣不到旁邊Component的頂點,而導致兩個Component的不連續,這裏我暫時使用了比較暴力的方法,所有的Landscape Comonent 在SetHeightData都不計算法線,而是在最後重新計算整個Landscape的Normal。 再看下修改後多選Component增加一個HeightField Noise的效果。 技術分享圖片 技術分享圖片 Height Data處理後,就是Layer Data的處理了,這裏把HeightField Noise 改成 HeightField Mask Noise,對Landscpae的4個Layer的Mask做噪聲處理,和之前Height Data導入時一樣,也會有Component之間的接縫問題。 技術分享圖片 這種分割圖接縫的問題,以前用WorldMachine做Tile Mask時也經常遇到,也就是Tile邊緣之間共享頂點的問題。用WM可以少輸出一圈邊緣的Map的方法來解決,在UE4裏也可以使用類似的方法。 技術分享圖片 在調用的LandscapeEdit.SetAlphaData參數上,把Stride比默認的寬度減少1(XSize - 1),就可以不傳邊緣的Mask Data進去了。
 
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(), XSize - 1);

  

再看一下效果,接縫的問題基本上已經解決了。 技術分享圖片

總結

至此,Houdini技術體系的幾個問題的基礎解決方案已經完成,後面的文章會逐漸傾向Houdini的地形實際制作部分。 技術分享圖片 而這些技術案例,大多要基於這個閉環+可選組件的方式來實現,隨著技術介紹流程,我也會在Github上定制一個類似Far Cry5的UE4 Houdini Engine版本,希望大家多提寶貴意見。

Houdini技術體系 基礎管線(三) :UE4 Landscape Component的多選支持 下篇