这两天挪威大神不在,感觉有点寂寞。刚刚学习完他的一个牛逼工具Volume Lattice。鉴于他直接把这个工具已经拿到Orbolt里面卖钱了,我在这就只讲讲自己的学习理解,代码什么的就不在这上了,何况要是理解了方法其实零代码也能够自己实现出来。这里是他的工具连接,给这位牛逼的外国师傅做做广告:Volume Lattice
效果图:
这个工具的思路主要是使用点云来代替voxel,通过拉伸点云之后,求出每个点的位移向量,再把这个向量值转变为体积,最后使用这些向量来计算density新的位置。
制作步骤这样的:
1:在需要拉伸的volume里面均匀随机的撒上点,这里可以使用PointFromVolume和PointJitter来完成,密度可以是和需要变形的volume分辨率相关,当然还有很多别的方法,自己玩吧。这样算是完成了将volume暂时转化为点云的过程,其中点云的密度与之后拉伸的精确程度相关性不是特别大,因为晶格变形的时候我们的晶格控制点也不会太多,但是点云数量也不能过于稀疏。另外如果需要做变形动画的话,建议还是把点云数量开高一点,要不然动画的时候会有抖动。
2:通过自带的lattice来拉伸点云。这里挪威大神自己做了一个节点来完成这一步,仔细分析完之后其实就是lattice里面的Liner Interplolation,虽然个人觉得自己再写一个确实有点多次一举,但是学习的乐趣就在于总能发现一些以前从来没接触和了解的东西。为了了解他的方法,我找到了wiki里面对于双线性插值的方法(bilinear interpolation),了解到只要我们使用的是通过盒子来做Lattice的方法其实就是传统图像学里面插值方法的一种,最常见效率和效果平衡最好的是三线性(因为我们有三个维度)插值方法,另外还有spline插值方法,Houdini的lattice里面对于比较柔和的插值方法使用的是Bezier插值方法。
下面的图解释了双线性插值在平面维度的原理,图有点简单纯靠悟性了,如果不懂这里是wiki链接:Bilinear Interpolation
3:言归正传,做好点云的lattice之后,用wrangle也好还是vop也好,把每个点对应的位移displace计算出来并写到被变形后的点云里面。用变形后点云的Bounding Box来生成类似步骤1的点云,把拉伸后点云的displace传递给新点云,这样displace就以点云的形式充满的新的box,再用VolumeFromAttrib把displace变为volume值。值得一提的是因为不论怎样点也是不会完全填充在体积里面的,所以当直接把Attrib转换给Volume后,其实Volume里面该值是很不均匀的,在同一个切面下有些地方有,有些地方完全没有。这种情况下就需要使用Volume Blur来把数值做个均匀,测试的结果是使用两层blur效果比较好,第一次将没有数值的地方使用Minimum来填充上,第二次是用Average的方法让数值变化更加柔和。其实blur的效果好不好直接决定了之后Volume变形的效果好不好,因为这个步骤才真正意义上把变形的数据填充到了整个volume里面:
4:新的box大小就是变形后volume的大小。依照这个大小新建一个空的volume,在vop或者warngle里面用Pos先读取displace的volume值得到一个向量v,所以我们知道当前Pos的density值的位置是在原来Pos - displace的地方。就着算出来的这个向量值在原来没有变形的volume里面取出density值赋予到这个新box在pos的位置上。这样就完成变形了。
总结一下:
关于volume里面的值是和sop里面几何体下面的值是有很大区别的,voxel和点的区别在于,当volume的盒子和division确定后每一个voxel就是固定的死的,这个有点像我们显示器上的像素点,数据(颜色值之于像素点)变与不变与voxel无关,它只负责表现出来。而点的所有数值是跟着点走的,而点的位置也是通过P的变量写在自己身上的,所以针对与某一位置的数据如果是在点上,不论位置怎样变换,我们只要找到那个点就能拿到所有的数据,这样通过点为媒介我们可以说某一个值变换了位置。而在voxel里面,所有的数据都没有一个像点一样的ID来确定当前voxel里面的值是从哪转移来的。解算出来的rest在一定程度上可以充当这个ID,但是也不是绝对的稳定,这也是为什么pyro solver里面会有一个rest2来当替补融合rest。另一种方法就是从volume中附带的vel速度来估计其它值是怎样转移的,但毕竟帧与帧之间我们只能以线性匀速来估计,但是解算的时候速度肯定不是线性的而且大部分时间也不是均匀的。