《Book of the Dead》风与植物交互分析

Kirk 腾讯互娱工程师

导语:《Book of the Dead》是由Unity官方制作的一个演示短片,其中有大量的植物,不仅渲染上有着照片级的真实感,而且风与植物的交互也非常的自然。其所有的自然资源都是来自照片扫描技术,而且使用了HDRP高清渲染管线,场景在Unity Asset Store上可以下载得到。本文主要分析短片中风与植物交互的原理。

 

 

---

 

▌支持的植物结构

 

场景中,对于风与植物交互的模拟,支持三种结构:

 

• Hierachy Pivot:层次嵌套Pivot,用于模拟树或者其他有多重层次结构的植物

• Single Pivot Color:单Pivot,用于模拟草

• Procedural Animation:程序动画,用于模拟浮萍等无pivot的植物

 

 

对于树的模拟最为复杂,它属于Hierachy Pivot结构,最多支持3个层次嵌套:

 

• 主干,连接地面

• Level 0分支,连接着主干

• Level 1分支,连接着Leval 0分支

 

 

---

 

▌代码入口

 

本文重点分析Hierachy Pivot结构的实现原理。风与植物的交互一般用程序顶点动画实现,随意找到一棵树的shader,顺藤摸瓜可以在VS中找到如下代码:

 

可以看到,每个顶点的uv3通道中存的是pivot信息,即该顶点受哪些pivot影响。

 

注意的是,这里的uv3是float3类型。

 

Hierachy Pivot结构的植物,最终调用的是AnimateVegetationHierarchyPivot。

 

---

 

▌PivotData解码

 

pivotData是float3类型,先用asuint转成uint3,一共是32x3=96个bit,分成两段前后48bit,分别存Pivot0和Pivot1的信息,分别用UnpackPivot0和UnpackPivot1解出来。

 

 

Pivot0和Pivot1刚好是对称排列。

 

接下来分析Pivot0是如何解码出来的,48bit里面,高32bit存Pivot Pos,低16位存Pivot Fwd,细节如下图所示。最终解出来的Pos是模型空间的坐标,树的建模应该是树干的根在模型空间的原点,Pos.x和Pos.z是有正负的,而树只能向上长,于是Pos.y必然是大于0。对于一般的树而言垂直方向范围一般大于水平方向的范围,于是用12bit保存Pos.y的值,稍微比x和z多2个bit的精度。

 

满足packedData.y & 0xFFFF0000时,即高16位有值时,代表有Pivot0的信息,才需要解析。

 

其中Pos.x用UnpackFixedToSFloat解出来,10bit实际存的是百分比[0, 1],由于x可能是负数,编码时把[-1, 1]映射到[0, 1],于是这里把[0, 1]反映射回[-1, 1],再乘以传入的range,可以看出Pos.x的范围是[-8f, 8f]。从其他硬编码的参数可以看出,树的建模尺寸是长宽16x16,高是32。

 

Fwd是分支(树干或者树枝)的方向,由于是单位向量,所以只存了x和z分量,y分量可以通过公式反算出来,开方后丢失了符号信息,于是用1位存符号。

 

---

 

▌整体流程

 

伪代码如下所示,有点跟骨骼动画类似,顶点受骨骼的变换影响,而每个骨骼会受其父骨骼的变换影响,最终顶点受骨骼的级联变换影响。树的顶点至少受主干的影响,因为任何顶点肯定要么是属于主干或者属于其他分支,而其他分支必然直接或间接连着主干,最复杂的情况是顶点在level1分支上,level1分支连着level0分支,level分支连着主干,需要计算累计变换。

 

-主干受风影响

 

对于每个枝干受风吹后弯曲程度,由如下变量控制,整体的弯曲程度可以由Wind Elasticity Lvl x系列变量控制,其中Lvl B是主干。

 

树被风吹有个特点,离地面越远部分,被吹弯曲的越厉害,所以会有个缩放系数来控制旋转量,地面处为0(树根),离地面越远的部分这个缩放系数越大。有种预烘培做法,是用顶点模型空间的y除以整个树的高度,计算出缩放系数并把它烘到点色或者其他通道上,这里的做法是运行时计算,用变量_WindRangeLvlB调节受风的范围,它是模型空间的量,其实跟预烘培的效果差不多。lvBElasticity是最终的弹性缩放系数。

 

对于风吹草的模拟一般在顶点加上风力方向的偏移就可以得到比较好的效果,因为一般草都比较矮小,但是对于树这种比较高的复杂结构,用草的方式模拟会有种树被拉扯变长的感觉,所以一般的方案是用旋转代替顶点偏移。

 

lvBWindAxis为旋转轴,windFwd与lvBFwd如果同向或者反向时候,主干应该是不会旋转,后面的枝干level 0做了这种情况的修正,主干这里可能从设计上就不会有垂直于地面的风向吧。

 

然后就是把旋转作用到顶点的pos和normal上,旋转的锚点是世界空间下的objectRoot,应该是模型空间的原点。

 

 

-支干Level0受风影响

 

逻辑基本与主干差不多,不同的地方是lv0Fwd的方向用lv0BaseGustWind和lvBDistScale做了调整,猜测是为了让弯曲旋转更自然。

 

旋转角度lv0WindRotAngle根据windFwd和lv0Fwd的是否平行,进行了相应的衰减。

 

 

-支干Level1受风影响

 

Level1是树最外层的部分了,整体流程与LevelB和Level0差不多,另外多了一些扑动的处理,树的外端末枝(树叶或者小树枝)被风吹的时候往往是有较剧烈的摇晃,而且呈一定的随机周期性运动,这部分计算出来的是顶点偏移,并在最后的旋转前先加到顶点坐标上。

 

Reference: 

 

1.https://unity3d.com/book-of-the-dead

2.https://docs.unrealengine.com/en-US/Engine/Content/Tools/PivotPainter/PivotPainter2/index.html

阅读与本文标签相同的文章