第三节 实 例 操 作
基本的粒子控制知识已经学得差不多了,在继续学习之前我们做一个特效来检验一下学习的效果如何:
这个特效很简单,一个泡藤片落入水杯里面。
泡藤片落入水里需要用到的粒子特效是:
1.落在水面上,会产生涟漪,晃动(用柔体);
2.水面会有水花飞出来,水花粒子越来越小(粒子发射粒子,用Ramp控制粒子大小);
3.水花落在水面上又会产生涟漪(粒子发射力场);
4.泡藤片在水里发泡泡(物体发射粒子);
5.气泡产生的时候很小,随着时间的增加会变大,并且上升,越上升体积越大,到了水面消失(表达式控制大小、速度)
综上所述,我们使用了很多的知识,对粒子的控制使用了几乎所有学过的的手段。在开始制作动画之前先看一下最终的效果:
里面的泡泡好像太多了,不过重在有泡泡就好,多少都好办。
接下来就开始制作了:
首先是制作一个玻璃杯,然后再制作一个泡藤片,这都非常简单,当然了,还要一个作为水面的圆形polygon面。
杯子是使用一条曲线Revolve出来的,泡藤片就是标准的Torus模型,这都很简单,nurbs和polygon都是可以的,但是水面一定要使用polygon,虽然使用nurbs能够更好的模仿水面的效果,但是圆形的nurbs面是不存在的,因为nurbs一定是有U和V方向的,而圆没有,虽然你可以做出一个看上去是圆形的nurbs面,但其实它还是方的,并且在制作柔体的时候出现非常致命的问题,我们这就研究一下:
要制作圆形的nurbs面通常使用的方法是制作一个nurbscycle然后使用命令surface→planar;然后使用rebuildsurface将面的细节增加:
使用Planar建模:
这个面看上去是完全没有问题的,可实际上planar在这个命令其实就等同于:先创建一个方形的面,然后再使用trim剪切得到圆形,这一点我们通过创建柔体就可以得到证实:
我们使用命令:soft\rigidbody →creatsoftbody(默认属性),可以看到:
很明显了,粒子和通常一样,附着在每一个控制点上,可是控制点并不是和wirframe一样的布局,而是很奇怪的,组成了一个方形的平面。
所以,很明显圆形的nurbs面是通过方形的面切割而成的。
如此少的控制点,根本就无法满足我们制作水面效果的需要,所以我们选择了poylgon来制作水面,只需要把这个曲面转换成poylgon就可以了:使用命令modify→convert→nurbsToPoly来转换具体参数设置如下图。
水面的模型做好了以后就要进入正式的制作过程了,当然首先还是制作柔体的水面了,选择那个水面,然后使用soft\rigidbody→creatsoftbody制作水面:
将goalWeigh设置成为1的目的是为了能够更方便的通过控制goalPP来控制最终的粒子目标全重,因为总的目标权重=goalweigth乘以goalPP。
制作好了柔体,这时候柔体粒子是百分之百的吸附于面上的点的,所以接下来就是改变goalPP的值,要知道1乘以任何数就等于任何数的,所以只要改变goalPP就直接控制了粒子的最终目标权重。
怎样改变goalPP?很简单使用soft\rigidbody→paintsoftbodyweigthtool来绘制goalPP的值:
如上图所示,将中间的goalPP设置成为0.1,最边缘的goalPP设置成为1,那么中间的柔体粒子将会受力产生运动,而最边缘的粒子由于目标总权重为1,而不受力的作用而保持静止,以达到我们想要模仿的一整杯水的效果。
接下来完成水面的制作:
选择柔体粒子,为其添加动乱场(turblence)参数设置如图:
此时播放的后果就是乱七八糟,和水杯里面的水面相差甚远:
原因就是很小的goal权重不足以抵抗动乱场(turblence)的力,所以粒子散了开来,要解决这个问题不能够增加goal的权重,否则就不会像水面那样起涟漪了。这时候就是要模仿水面的表面张力,分子间互相吸引的效果,再加上goal的力量将粒子拉回原来的地方,如此便产生了真实的水面的效果。
所以接下去就是为粒子创建弹簧(使用菜单soft\rigidbadies→creatsprings),具体参数设置如下图
再重新播放动画,水面的效果就出来了,可以不断调整动乱场(turblence)的大小来制作出不同的水面的效果,也可以为可以不断调整动乱场(turblence)的phaseX属性或者phaseY属性写一个简单的表达式:turbulenceField1.phaseX=time*2来产生水面流动的效果,不过在安静的水杯里面也不会有风吹水面的感觉的:
至此水面的制作过程结束了。
接下来是制作泡藤片落入水中的动画,这里我们就不用动力学的刚体运算了,我们使用最传统的方法来制作:key帧,这样能够更逼真的模仿药片落入水里,然后突然减速,向上再返升一下,然后慢慢地落到杯子的最底下,然后停止的过程:
在key帧的时候,我们通常会使用一种叫做“属性封装”的办法来进行工作。
也就是图中yaopian本身并没有被key帧,我们是通过群组的形式,分别对药片的translate属性,rotate属性进行key帧,这样很容易进行修改,并且还可以对模型本身进行改变,甚至是替换,而关键帧信息却不会丢失。
下面是封装药片的三个组的动画曲线:
药片的动画制作完毕以后,接下来就是制作药片砸入水面产生的涟漪或者说是把水面砸出一个水洞,在这里我们使用很简单的方法:
我们使用一个和药片直径相同的球体来碰撞柔体粒子,以此产生药片和水面发生碰撞的效果:
可以直接用鼠标中键在outliner里面把碰撞用的模型(pengzhuang)托放到药片模型(yaopian)的下面,这个动作和使用命令“parent”的效果是一样的。这样将碰撞体就会随着药片的动作而动作了
接下来就是制作碰撞体和水面产生碰撞的动画了,选择柔体粒子,同时选中碰撞模型(pengzhuang);然后使用命令Particles→MakeCollide使得碰撞体和水面产生碰撞,看看效果(将播放速度调整到playeveryframe):
同时选中碰撞模型(pengzhuang)使用命令Particles→MakeCollide使得碰撞体和水面产生碰撞:
细心的调整弹簧的stiffness和Damnping的值,创建出你想要得水面的效果:
水面的碰撞效果还是很容易理解制作的,利用的是柔体粒子碰撞,原理还是很简单的,接下来要实现的效果将是水面产生水花的效果。
首先要确定水花是什么样子的,我希望水花是在水面向上凸起到最高点向下的时候产生一个水花柱,用来模仿真实的水面的断裂感。
要产生这种效果就必须要使用粒子发射粒子了,就是让处于最高点的柔体粒子发射出新的粒子模仿水花:
选择柔体粒子,使用命令Particles→EmitformObject,
参数设置如下:
结果是:水面柔体粒子又发射出了水花粒子:
这并不是我们想要的,我们希望:
1.发射水花粒子的是到达最高处的一部分柔体粒子;
2.不是一直发射,而是当这些柔体粒子到达最高处的时候发射水花粒子。
所以,我们使用命令Particle→Per-PointEmissionRates,这个命令可以为柔体粒子增加一个新的属性RatePP用来控制每个柔体粒子发射水花粒子的Rate:
我们可以先简单的为这个属性写一个RunTime表达式:copyOfshuimianParticleShape. shuihua1RatePP=0;//注,我的柔体粒子名称是copyOfshuimianParticleShape,水花粒子的名称是shuihua;
这个表达式的功能很明显了,就是让发射水花rate等于0,所以就没有水花粒子发射出来了:
然后是解决到了最上面的时候,某一些柔体粒子发射水花粒子:
我们使用一个Locator来定位这个最上面的位置,从而很容易决定哪一些柔体粒子,在什么时候发射水花粒子了:所以目的很明确了:
如上图所示:
当柔体粒子在Y轴位置高于Locator的位置的时候,就让位置高于Locator的柔体粒子发射水花粒子。
为此将表达式作修改如下:
//将柔体粒子的位置信息赋值给变量$pos;那么$pos.y就是柔体粒子在Y轴上的位置了。
//判断如果柔体粒子在Y轴上的位置比Locator的位置低,那么不发射水花。
//判断如果柔体粒子在Y轴上的位置比Locator的位置高,那么发射水花Rate=100。
可以看到,我们要的效果实现了,粒子按照我们的想法发射出来了,但是粒子的形态并不是我们想要的,我们把粒子的类型改变成Blobysurface类型的,这种粒子是专门用来做流体效果的,关于这个粒子具体我们会在后面的章节里面学习。
使用Blobysurface类型,并且调整radius属性:
接下来是调整水花的动态:
水花应该是发射以后向上移动(使用表达式给水花粒子一个向上的初始速度)并且向内集中,然后再向下落下,这些效果使用一个volumeAxisField(体积场)力场就可以了:
首先是给水花粒子一个向上的初始速度,因为水花是要继承水面上升的速度的,然后再受重力影响向下落的,所以选择水花粒子的给它的velocity(速度属性)一个creation表达式:
shuihuaShape.velocity=<<0,1,0>>;//使得粒子在Y轴向上有一个初始化速度。
有了初始化的速度以后,粒子在发射以后就会沿着Y轴向上移动:
接下来就是要让粒子在上升的同时受到力的影响转而向下落,为此使用一个volumeAxisField(体积场)就足够了:
创建好了体积场,接下来就是把它放到应该放的位置上面:它的体积应该至少把水面和水花粒子包含在内。
水花粒子在受到力场(AlongAxis=-1)影响的情况下,会上升一个很短暂的时间,然后就往下落,并且由于力场(AwayFromAxis=-0.06)的影响会向内集中,这个值不能设置太高,否则粒子在向中间集中了以后又会由于惯性的原因再次扩散开来:
解决了粒子的运动问题以后,我们开始着手解决水花落在水面上对水面的影响:
水落在水面上应该会对水面有一定的影响,至少会使“砸”得水面向下凹进去一点,所以我们似乎是又需要用到碰撞了,但是粒子之间是不能碰撞的;也就是说水花粒子和水面粒子是不能直接发生碰撞的,那我们怎样使水花粒子将水面“砸”下呢?
我们是使用水花粒子发射空气场(使用了maxdistance影响范围的力),然后再使用力场影响水面粒子,就可以产生着这种效果了。
选择水面粒子,然后使用菜单命令:Fields→Air,为水粒子创建空气场:
创建空气场的菜单:(www.xing528.com)
具体参数设置:
请注意,这里使用了MaxDistance=0.5是希望距离水花粒子0.5以外的水面粒子就不受空气场的作用了。
接下来,选择创建好的空气场(Air)然后同时选中水花粒子,然后使用菜单命令:Fields--UseSelectedAsSourceofField;
这时候,打开Outliner会发现空气场(Air)变成了水花粒子的子物体了:
这也就意味着,空气场(Air)现在是由水花粒子发射出来的,接下来之需要最后一步,也是最重要的一步:打开空气场(Air)的ApplyPerVecter属性,这个属性决定了空气场(Air)是被粒子的行节点质心发射出来还是从每一个粒子身上发射出来,
当ApplyPerVecter属性设置为off的时候,空气场(Air)是简单的跟随所有粒子的平均位置(质心)移动的;
当ApplyPerVecter属性设置为on的时候,空气场(Air)就会从每一个粒子的身上发射出来。
设置了ApplyPerVecter属性设置为on以后,就可以播放动画动画了:
可以看到水花粒子已经对水面产生了作用了。
解决了水花和水面的碰撞问题以后,就只要简单的处理一下水花的生命周期和粒子大小的问题了:
水花应该是在碰撞到水面以后逐渐的就消失了;水花粒子的大小似乎是可以不变,也可以先大一点然后再小一点就可以了。
看来很简单了:让水花在产生的时候大一点,然后过一段时间正好在进入水面的以后越变越小,就可以解决这个问题了。
随着时间粒子越来越小,只要使用ramp就可以了。
所以我们使用Ramp控制每一个粒子的缩放属性,在这里就需要在PerParticlesAtrribute栏目里面找到那个可用于控制每一个粒子半径的属性了,默认的是没有的,因为BlobySurface粒子的半径控制是私有属性,所以需要单独加载属性:
点击General按钮
在弹出的窗口里面选择RadiusPP
可以看到,在PerParticleAttribute栏目里面增加了RadiusPP的属性:(这个属性是控制每一个粒子半径的属性)
在这个属性上面单击鼠标右键,在弹出菜单里面选择CreatRamp:
再看场景里的水花粒子突然变得好大,那是因为数据映射的值没有修改。
再次这个属性上面单击鼠标右键,这次选择:
然后就会进入数据映射的编辑属性面板:
在这里可以修改最大值和最小值,最大值就是水花粒子产生的时候的大小,最小值就是水花粒子消失时候的大小,将其修改为合适的值,一面播放一面就可以交互式的设置。
在播放动画的时候,我们会发现粒子很快就到了最小值的大小了:
这是因为粒子的生命周期,由于使用了Ramp默认的设置成为了1,我们需要手工将其调整为适当的大小,使得水花粒子正好是在水面以下开始消失:
接下来是调整水花粒子在生命周期里面的大小变幻了,这个要调整那个Ramp的颜色位置就可以了:(还是在RadiusPP上面单击鼠标右键)
选择EditRamp进入编辑面板:(很简单白色代表刚才修改的最大值,黑色就是最小值了,在时间轴上返回从下至上的颜色)
于是,得到了水花粒子的动画:
水面水花的粒子的运动都已经做好了,现在就剩下泡藤片的自身的气泡的运动没有做了,那么现在就来做泡藤片发出的气泡:
泡藤片发出气泡是很简单的物体发射粒子就可以了,选择泡藤片模型,然后使用菜单命令:Particles→Emitfromobject:
参数设置如下:
当选择Create以后会有错误发生,那是因为我们的泡藤片模型下面还有一个用来碰撞的球体,所以先把那个碰撞用的球体拿出去(使用Uparent命令),然后再确定一次,就创建了面发射器成功了,然后再把那个球体用鼠标中键拖到泡藤片的下面(或者使用Parent命令)。
虽然很麻烦,但是没有办法。
气泡发出来了:
气泡很简单地就从泡藤片里面发了出来,我依然使用BlobbySurface粒子,接下来就控制气泡的运行轨迹了:
首先,气泡是在落入水中才开始发射的;
其次,气泡发射出来的时候是很小的,随着时间的增加而变大的;
最后,气泡会向上升起,到了水面之前就会消失,而且水泡越大上升速度就越快。
解决方法是:首先,为泡藤片的气泡发射器的Rate设置关键帧,使得气泡粒子从落入气水面以后开始发射;
利用表达式控制气泡的大小变化(RadiusPP)、存在时间(LifespanPP)、以及在Y轴方向上的速度。
然后:使用表达式控制粒子发射出来很小,时间越长半径越大的效果。
这里就必须要增加一个RadiusPP的粒子属性:
再为其添加一个
CreationExpression:
particleShape1.radiusPP=rand(0,0.02);//这样粒子在发射出来的时候是非常小的,并且并不是同样的大小,这样就不会产生同样大小的气泡,更加真实了:
先看效果:
然后要为其添加一个RuntimeExpression使得气泡粒子在上升的过程中逐渐增大,最简单的表达式当然就是:
particleShape1.radiusPP+=0.002;//也就是每一帧气泡的半径都会增大0.002的大小。
但是这样做,气泡粒子开始还有大小之分:
可到了后来就全部都一样大小了:
因为,每个气泡粒子的增加值都是一样的,最后差值只有0.002左右,实在是看不出有多大差别了:
为此,我们必须改进表达式,我们必须使得不同大小的气泡粒子的增大速度不一样,为此我们为气泡粒子增加一个PerParticleAttributes:
这个粒子属性是自己创建的,用来存放每一个气泡粒子在产生的时候的半径大小,然后在运行表达式里面(RuntimeExpression)使用:
来使得每个气泡粒子的变大的速度都不一样,大的气泡变大更快,小的气泡变大就更慢了。
所以CreationExpression就需要增加一句:
//将产生时的气泡的半径信息给粒子属性randPP。
所以,现在的表达式情况是:
可以看到气泡的大小始终是不一样的,这正是我们想要的效果:
接下来是水泡上升的技术问题,我们希望水泡在Y轴的方向上加速上升,并且是半径大的气泡上升的速度快,半径小的上升速度比较慢。
所以很容易想到把气泡的半径(RadiusPP)值和Y轴上的速度(particleShape1.velocity的Y分量)联系起来,所以表达式就是:
float $v=particleShape1.radiusPP*8;//定义一个变量$v作为速度变量
particleShape1.velocity=<<0,$v,0>>;//把$v赋值给粒子的Y轴上的速度分量:
水泡上升的效果如下:
还剩下最后两个问题。
1.气泡粒子到了水面消失;
2.气泡粒子在水平方向上没有运动。
先解决第一个问题,很简单,使用表达式控制气泡粒子的生命周期,使得气泡粒子在Y轴到达某一个高度的时候,就消失:
于是,我们建造一个locator命名为:Max_distance,就使用它的Y轴位置信息作为参考高度,于是将这个Max_distance放置在杯子水面的下方:
在写表达式之前不要忘记把气泡粒子的生命周期形式调整为LifespanPPonly:
那么气泡粒子的生命周期表达式就是:
(RuntimeExpression):
float $Max_distance=Max_distance.translateY-(particleShape1.randPP*50); ///其中Max_ distance. translateY是那个Locator的Y轴的位置,randPP就是每一个粒子产生时候的大小,这样产生的值就都不会一样,并且气泡比较大的就先消失。
好了,还剩下最后一个细节就是气泡在横向的混乱运动,这简单,给一个混乱场(Turblence)就可以了。
气泡效果:
气泡的动画做好了,为了避免产生气泡粒子穿透杯子的意外情况,我们为杯子和气泡粒子制作碰撞事件,使得碰撞后的气泡粒子立刻消失:
先选择气泡粒子,然后按住Shift选择杯子,使用菜单命令Prticles→MakeCollide
好了,现在所有的细节问题都搞定了,剩下的就是材质了:Blobysurface粒子可以使用材质,并且能够被渲染出来,默认情况下是一个个的圆球。
对于Blobysurface粒子的知识点,由于该粒子使用非常频繁所以,决定使用一章的空间充分的学习。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。