Houdini技术体系 基础管线(三) :UE4 Landscape Component的多选支持 下篇

背景

上篇中,咱们介绍了如何修改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也必须是Nx M这种连续的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版本,但愿你们多提宝贵意见。
相关文章
相关标签/搜索