龙卷风的实验断断续续持续了较长一段时间,主要是想通过这个方式把流体的速度场和力场好好磨一磨,之前一直觉得流体的形态可控性不高,所以希望能找到一些方法或者经验能够摸透流体的运动。
说到龙卷风大家都不陌生,在特效圈子里面也是一个非常经典的案例,做好龙卷风能一定程度上体现出一个特效师在多个方面综合的素质体现。现在市面上的方法确实不少,各种软件在龙卷风的时候也有各种不一样的特性体现,但最终表现大多都离不开流体和粒子这两种。今天我就梳理一下,通过流体和速度场搭建龙卷风效果的不同方法和横向的效果比较。
在讲应用之前还是先从龙卷风讲起,这段时间也是查了不少资料。其中比较有用的就是下面的几张图,以及在国外网站上找到的一个研究生的 毕业论文 。这个学生也是想用houdini做一个可控的龙卷风,但是全片下来除了把现有的龙卷风类型讲的比较透彻,在应用那一块基本上也可以直接忽略了,可想国外的研究生有些过得也是比较水的。
第二张图是从wiki的vorticity中截取的,这个截面比较直观的体现了龙卷风风场横截面的速度与中心的关系了。
原理搞懂后不难得出,龙卷风的运动特性是涡流的一种体现,很多时候飞机引擎的前后也能出现类似小旋风的情况也就是这个道理。所以在houdini中应用主要就是看怎样认为的形成类似涡流的速度场。
1:先讲第一个方法
这个方法也是沿用的国外某个教程的方法,简单明了,也适合很多人的理解,下面是速度场的骨架:
这个方法主要是利用了龙卷风具有螺旋线的运动特征,所以最直观的搭建了这样的骨架,但是流体通过这个表现出来还是会有一些些的小瑕疵,那就是几个螺旋线都是最后变成管道的,而管道与管道之间就会形成B一样的外形凹槽,这个槽有可能会在流体的运动中体现出来而出现类似螺丝钉的样子。这个有好也有坏,如果把点云扰乱了,这种效果就不会那么明显了。
这是用这个方法生成的一版效果,精度比较低,笔记本就是烂,凑合看吧。
2,第二种方法完全是自己试探性的玩法,也就是几个平行的管道加一点恼乱形成的平行高速运转的旋风效果,这个方法可以使用在已经稳定的龙卷风身上。
这个方法能让流体在速度场类非常稳定的跟着扰动的变化而更改位置,效果还不错,能够适用在比较魔幻的效果里面。
下图的这个效果应为把turbulence加大了一点,结果就像有妖气了一样:
再来张福利:
3,第三种方法是直接使用一个运动轨迹的外壳来确定速度场方向的,这个连续性比较强,而且运动的外观也类似第一个方法那样可控性比较强。
效果图:
4,第四个方法,我本来以为是应该最好的方法,因为是用程序写的力场,速度涡轮和大小都非常类似真是物理中的样子,而且是整个box里面都充斥这非常精准的速度值。但是效果不尽人意,也许是我深挖的不够,目前来说就是效果趣味性不强,也许我加大扰乱场可能会有很大改观,这里我就不再做这个测试了。所有类型的工程文件都在文章最后,有时间点朋友可以拿着继续测试下去,也很欢迎有更好的结果出现。
程序是在vex里面写的,整体来说不难,得出的矢量场也感觉没太多问题,也许就是太没问题太规则了 :P
1 float worldScale = chf("worldScale"); 2 float curlExp = chf("curlExp"); 3 float lengthExp = chf("lengthExp"); 4 float upScale = chf("upScale"); 5 float upExp = chf("upExp"); 6 7 8 9 int handle = pcopen(1, "P", @P, 1e6, 1); 10 11 vector guidePos = set(0,0,0); 12 float radius = 0; 13 14 //get skeleton guide position and radius values 15 while(pciterate(handle)){ 16 pcimport(handle, "P", guidePos); 17 pcimport(handle, "radius", radius); 18 } 19 20 //get the curl direction 21 float distance = length(guidePos - @P); 22 23 vector front = set(0,0,0); 24 if(distance > radius){ 25 front = normalize(guidePos - @P); 26 } 27 vector up = set(0,1,0); 28 vector right = cross(front, up); 29 30 //define the curl bias from the edge to the center 31 vector minBox, maxBox; 32 getbbox(geoself(), minBox, maxBox); 33 float maxRadius = min((maxBox.x - minBox.x),(maxBox.z - minBox.z))/2; 34 float centerWeight = fit(distance, radius, maxRadius, 0.95, 0.02); 35 36 float frontBias = pow(centerWeight, curlExp); 37 vector curlForce = lerp(front, right, frontBias); 38 39 //define the up direction vector 40 vector empty = set(0,0,0); 41 float upBias = pow(centerWeight, upExp); 42 vector upForce = set(0,0,0); 43 if(distance > radius){ 44 upForce = lerp(empty, up * upScale, upBias); 45 }else{ 46 upForce = @P - guidePos; 47 } 48 49 50 //combine all forces together 51 float lengthBias = pow(centerWeight, lengthExp); 52 vector mainForce = (curlForce + upForce) * lengthBias * worldScale; 53 54 @N = mainForce;
最后一个方法也许能够用来物理模拟之类的吧,但是单看效果来说就是渣渣了。不过我还是固执的认为这只是暂时的,因为他的形态是最没有人工痕迹的,之后再好好琢磨整一整应该也能出好的结果的。
聊了这么多,贴图也贴累了。个人来讲比较喜欢使用第二种和第三种方法。当然还有很多其他的方法是我没有考虑到的,很明显这篇博客就没有包括粒子速度场,没讲的原因主要还是觉得粒子本来形态的可控性就没有固定点云的形态好控制,如果再使用粒子来驱动流体,简直就是给自己找麻烦,所以这里我也直接忽略了。但是绝对不排除有大神直接玩得转的。还有一个大方向是直接使用粒子来模拟,这篇文章讲的是流体,所以这个方法也不在这讲了。
前前后后这些实验一共花了十天左右的空余时间,感觉还是挺值得的。也许上面所有的方法都还是有瑕疵,但是实验的过程就是一种探明道路的过程,重点不是你走到哪了,而是自己知道怎么开路了。
接下来看有时间吧pyro里面的东西好好捋一下。所谓的可控性就是你知道程序在底下自己在干嘛,千万别被他一堆一堆的参数给蒙住了。
看到这,你就可以接源文件了 :P