背景
前一节里,解决了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里就能完成闭环的操做。
方法一,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就能够了。
// 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边界问题。
在下篇中,咱们会针对这些问题,继续对Houdini Engine进行定制,以及提供针对不一样功能的HDA开发的示例。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">html