这一部分结合PDG来谈谈Houdini的地形工具。前半部分会讲一些我对Houdini地形工具的理解,算是作为学习笔记记录在此。后半部分会根据官方的教程整理出在Unity引擎中的实现步骤,比较繁琐,熟悉的直接无视就好。

过程化生成工具

目前主流的过程化生成工具都是两种思路,一种基于Node,另一种基于Layer。后者最大的好处在于可以快速上手,而且有一个所见即所得的编辑逻辑,例如Photoshop,Substance Painter都是这样。而基于Node的工具则需要使用者具有很清晰的逻辑,知道自己用这个节点要干什么,但这同时也大大便利了后期的修改和简化,通过修改每个节点的参数即可达到不同的效果。目前的Substance Designer,Houdini,blender都是基于Node的思路。

举个例子,在这篇文章中我们制作一个地形demo按照这样的顺序:

每个部分都由Houdini的数个节点组成,这些节点都有具体的参数来负责具体的功能,可以很方便的修改得到最终效果。同时,PDG模块可以将这些HDA模块统一在一个HDA下管理,分别控制不同的步骤,非常方便。

但是这并不代表基于Node就是好的,例如上面的layout绘制,比起根据节点一个个去计算,直接创建Editable Node去在引擎中刷地形是最快最方便的。

另外推荐一下官网上的Houdini Masterclass(可能要科学上网),详细的讲了H17中Terrian的制作步骤

地形侵蚀

Houdini的erosion演算法使用了hydro和thermal这两套结合,熟悉UE4的可以理解为将它原生的erosion和hydro erosion结合。

在erosion的解算节点中可以详细的看到在Houdini中erosion的做法,每一帧都先使用thermal去计算一个大致的腐蚀效果,然后根据water mask和height mask去做hydro erosion计算水的腐蚀,最后再去计算一个水的蒸发效果。

具体的腐蚀模拟可以看这篇论文:Fast Hydraulic and Thermal Erosion on the GPU

另外值得一提的是Houdini的Erosion是基于OpenCL的,所以应该也是可以用GPU加速的,但是尝试修改env文件并没有成功...如果可以用HDK实现那肯定能大大提高速度。

引擎测试

根据官网上给出的教程,这里我自己实现了一下,发现很多坑....(吐槽一句Houdini Engine for Unity真的太难用了)

地形笔刷:

在一个subnet下建立两个geo节点,一个作为绘制,一个作为导出。整个subnet会用HDA导出在引擎中使用

先用一个grid节点作为整块地形图,将size和rows作为参数

之后为我们的笔刷工具创建两个属性,density和scale,一个决定密度分布,一个决定绘制大小。并使用random节点对scale做一个小范围的随机(当然也可以直接用rand函数),笔刷则用edit节点实现

另一侧,创建tube几何体作为我们的山体,Height和Radius Scale是传出的参数:

我们用copytopoint节点将这个tube替换掉上面scatter后的grid,制造出山体的layout分布:

attribwrangle用来传递scale参数,scatter则受density参数影响

最后将grid和这个merge一下即可输出

然后是Exporter部分的做法,这边直接用了一个Object Merge取得几何体,然后用ROP节点输出到文件夹下。注意路径一定要用相对路径

ROP(Render Operators)

将整个subnet打包为HDA输出,作为我们之后的地形笔刷工具

在Node属性下激活Editable Nodes作为笔刷

参数选择之前painting节点下grid的size和row、tube节点下的radius scale和height以及Export节点中ROP的save操作和输出文件路径

在Unity下使用我们刚刚输出的HDA,点选options中的Enable Editable Node Tools

之后便可以使用笔刷工具在视窗里绘制了

笔刷这里有我们之前创建好的两个属性,density和scale

(吐槽一句这个笔刷超级反人类的,而且不!支!持!撤!回!

做好之后就可以点击右下角的export保存出去了

转换为地形:

上面绘制的只是一个大致的分布,这种效果肯定是无法放到游戏里的,而且纯粹的几何体也无法使用Houdini的各种heightfield工具,因此我们新建一个subnet来转换它为heightfiled文件,并对其初步处理

使用heightfield_project处理几何体为heightfield

可以看到很多边缘过渡部分都很不自然,具体的细节我们放到之后的Erosion部分做,先做些简单的处理让其更自然

blur的作用是减少地形锐利部分,distort对整体地表添加一个杂讯,而slump则使山脊过渡部分更自然

清除掉slump的mask即可输出为HDA了

Resolution是heightfield节点的size,effects下的都是上面blur和distort的参数

处理后的效果已经能看了,但是明显少了很多细节,因此我们需要使用强大的erode节点

地形侵蚀:

Houdini的erosion演算法在上面讲过,这里直接讲在Houdini中的做法

在SOP_HDA下复制我们之前创建的get_terrian_layout(获取PDG output的那个)节点,按照如下图连接heightfiled节点:

节点连接(1. 在heightfield_file删掉layering的layer name 2. 在mask节点那调整好分层比例)
Erode初步效果

如果是初次接触Houdini,可能对erode节点不熟悉,Houdini的侵蚀效果是根据下方的time line来推进模拟的,这里我们拉动下方的动画条来看看模拟之后的效果

35帧时的模拟效果

为了方便之后在引擎中调整,使用Time-Shift节点来控制侵蚀效果。记得按住Ctrl+Shift左键点击Frame属性删除关联

使用maskclear清除掉mask,这里为了显得不那么程序化,利用distort by noise做一些变形,最后还是用一个NULL节点标记输出

之后老办法Shift+C把刚才连的节点聚合起来,HDA输出

参数分别来自于timeshift、erode和distort节点

回到我们之前创建的Level_creation_top中的topnet下,在创建好的的hdaproceesor节点下再创建一个hdaprocessor,并将我们上面创建的HDA传入

然后就可以开始Dirty and Cook了吗?不对,一定要勾选这个!(血的教训QAQ,运行时发现一直无法创建geometry...)

最终显示结果

最后整理下整个topnet节点输出成HDA就可以开始在引擎中测试了,记得把所有hdaprocessor节点中HDA File路径下的JOB更替成HEU_ENVPATH_JOB

按之前的办法把TOP节点的HDA拖入场景。可以看到右边属性中有我们之前在Houdini中设置的layotu参数,把我们之前从Unity中导出的layout bego再传入

创建PDG链接,按下Cook等待结果(erode节点因为eode time较大会占用较多的时间)

输出的结果就是Unity的terrian

材质渲染:

(这部分我踩了好多坑,有Substance Designer的,有Houdini Engine的,仍然没得到满意的效果...希望能有个大佬和我讨论下)

我们在Houdini中给地形分层,做出不同的材质效果。同样复制我们之前的get_terrian_layout节点,创建出不同的mask来分层:

这里就不展开来讲了,按照项目的需求调整maskbyfeature参数即可(那个VEX节点mask = 1选中全部创了一个base mask),而每个copylayer节点都标注了一个层级,最终的layer信息:

这里官方教程创建了base,cliff和grass三个层级

为了避免mask之间的遮挡,我们使用blast节点清除不必要的mask,除了height mask是必须要保留,其他都可以清除掉

只保留了base,cliff,grass和必要的height

之后我们使用attribute create节点设置材质传入的参数,因为我们这里创建了三个层级,节点我们也同样使用三个

参数的命名和类型需要严格按照Houdini Heightfields to Unity Terrain中的标准

每个节点的Group属性需要区分开,以对不同的层级著色,例如grass的节点需要这么写

之后我们便可以Shift+C一站式把刚才做的创建为HDA了

参数为unity_hf_tile_size的value和unity_hf_texture_diffuse的string

在Unity中更新我们的top hda,可以看到多了texturing属性,Path就是我们贴图的相对路径

关于这个路径...实在是挺坑的,我这里解决方案就是把贴图放在和Scenes同一级的Resources文件夹下,然后把这个文件夹下的参数作为相对路径传入,注意图片文件的后缀名也要去掉。例如这样:

这个真的超级难用...

我这里直接从订阅的Substance Source里下载了三张地形的basecolor贴图来用,之后就可以在PDG Link中Cook结果了:

不知道能不能GPU加速这个erode过程
分层不规范,材质两行泪

地形分块:

为了减少我们每次渲染的区域,我们给地形分块,同样可以在PDG中做到

在SOP下建立Split节点,创建为HDA:

tilesplit节点的Tile count和number作为参数

在Top节点下新建hdaprocessor,把上面创建的HDA传入

在split的HDA Parameter里修改Tile Number的reference:

同时Work Items的Iteration属性也要改,来生成a x a大小的地形

Cook Node可以看到此时的task graph多了很多task

可以看到最终产生了16个(4 x 4)分块

在Unity采取跟之前同样的做法,最终得到的结果为:

总结

本次地形工具中的PDG中用了4个HDA Processor,一定程度上已经可以看出PDG流程相比传统单独HDA做法的方便之处

跟著官网的教程制作的同时发现了很多可以深入挖掘的点,例如Erode部分的演算法,Texture部分的mask布置,地形笔刷工具的改进等等,Houdini的Heightfield工具十分齐全,感觉一定程度上已经可以代替WorldMachine高度图导出,再结合强大的PDG流程,可以快捷的控制各个部分的效果。

虽然Houdini很牛逼,但是还要吐槽一下Houdini Engine for Unity的反人类,简陋的UI、无法撤回的笔刷、诡异的贴图引用路径、莫名其妙的HDA丢失......有需求的话,感觉还是自己写一整套方法和引擎去交互吧。


推荐阅读:
相关文章