背景
前一节里,解决了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;">
-
Houdini技术体系 基础管线(四) :Houdini驱动的UE4植被系统 上篇
背景 之前在<Houdini技术体系 过程化地形系统(一):Far Cry5的植被系统分析>一文中已经对AAA游戏中过程化植被的需求有了一定的定义,后续工作就是如何用Houdini开发功能 ...
-
Houdini技术体系 基础管线(三) :UE4 Landscape Component的多选支持 下篇
背景 上篇中,我们介绍了如何修改Houdini Enigne来设置单个Landscape Compnent的Height和Layer的数据,但原生Houdini Engine并不支持多选Compone ...
-
Houdini技术体系 基础管线(四) :Houdini驱动的UE4植被系统 下篇
背景 在上篇中,实现了使用Houdini在UE4里根据地形过程生成植被的最基本的原型.并且支持把植被在UE4里Bake成使用的HierarchicalInstancedStaticMeshCompon ...
-
Houdini技术体系 基础管线(二) :Heightfiled与UE4的无缝导入以及对World Composition的支持
Authored by TraceYang 前言 传统的制作做比较真实大世界3D关卡地形时,通常的采用的方式是把HeightMap和SplatMap(Layer Mask)导入到引擎的地形系统里 ...
-
Houdini技术体系 基础管线(一) : Houdini与Houdini Engine的安装
Houdini 下载与安装 在官网 https://www.sidefx.com/download/ 下载最新的Production Build 版本,当前是16.5版本,需要注册帐号 PS:公司内网 ...
-
Houdini技术体系 过程化地形系统(一):Far Cry5的植被系统分析
背景 在大世界游戏里,植被(biome)是自然环境非常重要的组成部分,虽然UE4里的也有比较不错的地形+植被系统,但相比国外AAA级游戏的效果,还是有不少的差距,简介如下: UE4的植被分为( ...
-
01-java技术体系基础
java体系基础 理论 编程语言: 系统级: C, C++, go, erlang ... 应用级: C#, Java, Python, Perl, Ruby, php 虚拟机: jvm(java虚拟 ...
-
测试需要了解的技术之基础篇三__持续集成持续交付DevOps
持续集成.持续交付.DevOps 1.容器技术Docker:容器技术介绍.Docker安装与加速配置.Docker基础命令.Docker搭建selenium.Docker搭建持续集成平台Jenkins ...
-
Houdini技术体系大纲
Houdini for UE4 Pipeline的系列教程,前言等想好再写吧
随机推荐
-
利用增量备份恢复因归档丢失造成的DG gap
故障现象:data guard归档出现gap,悲剧的是丢失的归档在主库上被rman备份时删除了,丢失的归档大约有20几个,数据库大小约2T,如果重建DG将非常耗时间,因此决定利用增量备份的方式恢复DG ...
-
python函数(五)
函数 1.函数基本语法及特性 背景提要 现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警, 你掏空了所有的知识量,写出了以下代码 ...
-
linq查询结果转换为指定字段类型的list集合
转换查询结果为ProductId字段的类型的list集合 (from s in _db.Mobile_TeamAction || s.ActionStatus == select new { s.Pr ...
-
caffe:编译时提示:unsupported GNU version! gcc versions later than 4.9 are not supported!
NVCC src/caffe/solvers/adam_solver.cuIn file included from /usr/local/cuda/include/cuda_runtime.h:76 ...
-
oracle 游标示例
declare iCount int:=0; sPath nvarchar2(200); tdzsh nvarchar2(50);begin for x in (select c.imgpath fr ...
-
Set集合中的HashSet集合
HashSet集合的特点:元素是具备唯一性的,每次存储都要先算出哈希值,看有没相同,没有相同的存储到相应的位置,如果相同则再判断存储进来的值是否与被比较的相同,如果相同,则不再存储,不同就存储 pac ...
-
python成长之路17
一:web框架的本质,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1.1:python实现: #!/usr/bin/env python ...
-
201521123008《Java程序设计》第八周实验总结
1. 本周学习总结 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 1.删除元素的时候从最后一个元素开始,避免删除元素后位置发生变化而导致有些元素没有删 ...
-
LeetCode题型分类及索引
目录 这是一个对LeetCode题目归类的索引,分类标准参考了July大神的<编程之法>以及LeetCode的tag项.分类可能还不太合理,逐步完善,请见谅~ 题主本人也在一点一点的刷题, ...
-
浅谈Windows用户帐户控制(User Account Control,UAC)
Microsoft一个事实:大多数用户都用一个Administrator(管理员)帐户来登录Windows.利用这个帐户,用户几乎没有任何限制地访问重要的系统资源,因为该帐户被授予很高的权限.一旦用户 ...