合成最终效果
[flash]http://player.youku.com/player.php/sid/XNTQyODMzMjgw/v.swf[/flash]
自学Naiad以来的第一个成品镜头,目的是测试Naiad配合其他软件做水的整套流程。结果还ok!
以下的制作解析部分,如果具备基本的Naiad、Maya、3ds Max及Nuke的软件基础,那么理解起来会更容易一些。
CubeFall Project的整个工作流程大致如下图1:
这个流程图展示了整个制作流程的各个节点,以及数据的格式和流向关系。由于这个流程图是在镜头完成后总结出来的,只能代表“总结”而非“探索”的过程。因此下面我会从制作之前的初步思路来一步一步推进到镜头最终完成。
制作之前的思路:
在制作之前,首先是无数次的观看ghost vfx为ilm做的“盒子砸水花”测试镜头。这是我见过迄今为止最棒的水体交互镜头,细致程度让人叹为观止。
在制作之前,我构思的大体思路是先用Maya制作小范围的水体容器,然后倒入到Naiad中解算出小范围容器的水的交互动态(解算数据包括主水体粒子、主水体mesh模型、浪花粒子),之后将粒子数据导入Maya进行浪花粒子的渲染,将主水体模型导入Maya进行海洋表面渲染,各自渲染完成后生成图像序列素材进Nuke进行合成。
这个思路看起来没有问题,实际上过于简明而忽略了太多的技术细节以至于不能预计到实际制作中碰到的问题。不过没关系,刚开始的计划总会显得稚嫩,但至少有了大体制作方向,于是开始进入制作的第一步:进Maya建立水体容器。
Maya中建立基本场景
越靠前的步骤越重要,如果第一步就不对的话,后面纵然再花时间也只能说是在错误的道路上越走越远。
在这一步中最最重要的地方是确定了整个场景的宏观规模,也叫宏观缩放。Naiad解算引擎是以现实世界中的物理参数为参考而设计的,因此场景的宏观缩放直接影响到最终效果是小石块掉湖里还是大陨石掉海里。
Maya的默认网格以厘米,而Maya的1厘米在Naiad相当于1米,我希望制作的效果是一个边长2米的立方体落入水中的效果,因此我在Maya中首先创建了一个边长为2厘米的方盒子。
如下图2的蓝色cube模型,Scale XYZ均为2。
(我2了一把,想标注这个立方体的体积为8m³,结果写成了2m³,很囧。)
边长2米的盒子砸入海中激起的交互反应会波及多远?这个和盒子下落速度等因素有关,需要预估。
最初我设计了一个比较小的水体容器(水池),后来经过Naiad解算测试发觉太小了,最后将水池半径改为约20米(Maya默认单位20厘米)后觉得差不多了(如图3),实际上最终解算还是有少许水花会飞出水池外,不过基本可以接受,实在不行就靠后期修吧。如果cpu和内存资源够多的话随便做多大的水池都可以,但是实际制作中要考虑成本与质量之间的平衡。
我制作的是圆形的水池而不是方形的,因为在考虑到立方体从水池中心砸入水中导致水花飞溅,可能以任意速度朝向任意方向的情况下,圆形水池是最节约计算资源的选择。
水池的底部是扁平半圆形状,最深处约为2.4米,这也是为了节省计算资源,之前会担心Naiad解算结果会使这种水池的设计失真,最后发现看不出来。
这一步一共建立了四个模型,一个水池容器、一个主水体存在区域、一个用作boundary layer的模型(后面步骤会说)、一个边长为2米的立方体。
主水体存在区域需要略微比水池边缘大,这能保证生成主水体的时候不会使水体与水池之间出现空隙,使主水体的初始状态稳定。
这一步初步搭建了场景相机,由于无法预测砸出来的水花的形态从哪个角度看最好看,因此该相机在后续步骤可以根据Naiad解算结果进行调整。
模型和相机分别导出为emp文件用于接下来的Naiad解算。
Naiad:主水体解算及导出粒子prt文件
主水体解算节点连接如图4:
图4中,Naiad视口中绿色线框表示的是水池模型,红色线框表示的是boundary layer模型。盒子下落最后我采用的是Naiad的刚体解算下落的,最后发现Naiad 0.6.0.69这个版本的Maya接口插件无法导入解算好的刚体emp文件,遂在Maya中单独给盒子k帧做的下落动画以匹配Naiad的刚体盒子下落运动。
镜头做完后才发现Naiad 0.6.1.43这个版本的Maya接口插件成功修复了这个bug 。
如果不考虑A、B、C三个我个人添加的部分,那么主水体的节点图就是一个经典的particle-liquid解算流程图,见图5:
如图6,A节点是一个Field-Nel-Channel节点,起的作用是speed limit,限制Field.velocity的值,避免出现粒子高速飞溅的情况。类似速度限制的表达式我在很多Naiad文件中都会用到,个人觉得是非常实用的功能。
如图7,C节点是一个Particle-Nel-Channel,用于获取粒子vorticity的强度值。老外教程里的方法通常都是使用一个Field-Nel-Channel和一个Particle-Nel-Channel来得到vorticity的矢量,我把表达式优化之后只需要一个Particle-Nel-Channel节点就可以得到vorticity的矢量,但图7中我写的表达式并不是去获取vorticity的矢量,而是获取了vorticity的强度。
我是基于以下两点:
①、在krakatoa中渲染出的vorticity其实只需要强度值即可用于后期校色;
②、vorticity的强度值是个标量,这样在写naiad缓存的时候要,存储一个标量值比存储一个矢量值数据量要少三分之二。
如图8,B节点区域的核心是boundary-Layer,起的作用是让水花飞溅到离盒子下落中心一定距离的时候,让水花和水面波浪运动减速,让其尽可能在到达水池边缘的时候没有水花溢出也没有水面波浪反弹效果。
三个Boundary-Layer的参数分别为:
(15 voxels/(@'Master Voxel Size'))*0.1
(7 voxels/(@'Master Voxel Size'))*0.1
(3 voxels/(@'Master Voxel Size'))*0.1
意义为分三级不同范围不同强度来控制particle-liquid接近水池边缘时候的减速程度。
在这里我用到了一个expression来控制Boundary-Layer的厚度,而不是使用绝对数值,这样的好处是无论Master Voxel Size怎么变,Boundary-Layer的物理厚度值都是不变的(肉眼可见的厚度)。
这样的表达式用法我也经常用在particle-liquid上来控制Surface SuperSampling的厚度,以下的解释也许能够帮助更确切的理解为什么要用到这样的表达式,如图9:
如上图所示,无论MVS值是多少,都能够保证粒子超采样的表层厚度都是不变的。
D节点为全局设置,如图10:
对于Global节点,Time-Stepping参数非常重要,在大多数教程上看到的参数基本都是Min Timestep 和Max Timesteps 都设置为1,这样的话Adaptive Timestep Sensitivity无论是什么值都不再有意义,每帧解算次数固定为1。
这样的设定适合教学、低精度预览和慢速流体的应用场合,一到高精度解算和高速运动流体的情况下,很容易出现动力学计算失真的情况。
在cubeFall这个镜头中,我的设置如图10中绿框所示。也许会有人质疑:Adaptive Timestep sensitivity为0的情况下,Max Timesteps设置为4有什么意义?
Adaptive Timestep sensitivity为0并不意味着每帧解算次数只采取Min Timesteps的值,在高速运动场合一样会采取更高的每帧解算次数来满足CFL condition(与FumeFx同理),最高的每帧解算次数以Max Timesteps为限。
“0”不代表Adaptive Timestep Sensitivity完全丢失敏感度。
以下是Naiad help中关于Time-Stepping Parameters的解释,以及附注的个人参数建议:
自适应每帧解算次数敏感度
译:这个参数是CFL condition的缩放系数,用于控制Naiad有多高的敏感度去处理快速运动物体。通常来说,物体运动越快,每帧解算次数就需要越高,也就意味着你需要将一帧分解成若干个小帧去计算。这个参数越接近1,表示Naiad将针对快速运动的物体采用更多的帧间计算次数来解算(最大次数取决于Max Timesteps)。明确的说,在一个流体模拟中,本参数设置为0代表允许流体在一次计算中最多移动10 voxel的距离,而如果本参数设置为1则代表允许流体在一次计算中最多移动1 voxel的距离,如果超过了移动距离限制,则将一帧分解成若干个小帧去计算(最多分成多少个小帧取决于Max Timesteps)。请注意:本参数设置为接近1的值将会明显降低解算速度,特别当你设置了一个很小的MVS值的时候。通常的建议是仅当你发现流体运动的时候发生动力学失真、体积莫名减少、整体细节有问题的时候,才去提升本参数的值。
个人建议:在MVS值很小的时候,本参数哪怕设置为0也一样很敏感,一点轻微的水体运动就会触发大于Min Timesteps的每帧解算次数,当MVS值很大的时候,本参数设置的很高也不一定会触发每帧多次解算!因此本参数根据MVS精度的不同、项目不同而需要设置不同的值。
最小每帧解算次数/每帧最少分解成多少个小帧
译:保证每帧解算次数最少不低于本参数,不管实际计算是不是需要这么多的每帧解算次数来满足Adaptive Timestep Sensitivity参数的要求。当你在想要强制Naiad进行一帧多次解算的时候本参数尤其有用。
个人建议:一般设置为1是没问题的,99%的项目本参数都应该是1。如果发现设置为1的时候动力学效果有问题,可能是设置了错误的Adaptive Timestep Sensitivity和Max Timesteps。
最大每帧解算次数/每帧最多分解成多少个小帧
译:每帧需要的解算次数最多不能本参数的值。当你觉得Adaptive Timestep Sensitivity参数要求过高的每帧解算次数的时候,本参数可以让你手动限制最大每帧解算次数。
个人建议:一般情况下在激烈运动的流体中该参数才具备实际意义(例如与物体碰撞需要飞溅水花的时刻)。本参数设置过高的情况下可能导致解算太慢、水花飞溅距离太远的问题,因此需要反复测试,从低值开始测试然后逐渐提高。
更细腻的控制方式:为该参数设置动画,在流体平静的时刻设置为1~2,当流体激烈运动之前(自行预测)将本参数设置为4-10。尤其在MVS很小、网格密度很高的时候,哪怕Adaptive Timestep Sensitivity设置为0的时候平静的水面运动都可能会触发大于1的每帧解算次数,此时用k动画的方式让该参数强制为1是节约解算时间的好方法,当然要记得在激烈运动之前k高本参数,流体回归平静的时候再降低回去。
因此本场景中即使Adaptive Timestep Sensitivity设置为0,盒子砸入水中的瞬间,每帧解算次数还是很轻松就达到了Max Timesteps。
当每帧解算次数大于1的时候,如何能够知道哪一帧进行了几次小数帧解算?通常的方法是在Emp-Terminal中的Per-Timestep Channels填入*.*,这样便会将小数帧的缓存数据悉数记录下来,不足之处是小数帧缓存会占用额外的空间,删除的时候也不方便删,而小数帧缓存一般情况下是用不到的,因此为了节约磁盘空间,我是这么设置的,如图11:
在Emp-Terminal中的Per-Timestep Channels填入一个根本不存在的通道名称,然后解算,最终的磁盘文件会产生一些非常小的文件,通过这些小文件可以很方便的看出来哪些帧用到了小数帧解算,要删除这些小文件也方便,按照文件大小一排序,把特别小的那些文件删掉就ok了。如图12