2012-06-07 16:53:10| 分类: RealFlow探索 | 标签: |举报 |字号大中小 订阅
创建泡沫(foam)和水花(spray)是RealFlow最常讨论的话题之一。已经有一些不错的方法来创建泡沫粒子,可以满足不同的需求。
尽管这样,我还是想用创建泡沫做为开始。因为这是一个非常好的例子,来应用你前面所学的所有知识。接下来的,我会一步步讲解写出脚本,你会从这一个个小步骤里慢慢学会应用所学的知识点。(译者注:按我学习经验,确实在做出本案例后才突然发现,啊,我尽然会用Realflow Python脚本了。希望更多人在学习本节后有我一样的体验。)
7.2自然界的泡沫(foam)和水花(spray)
泡沫其实是表面互相分隔的小气泡。为了能形成分隔,需要用表面活性剂(tenside)来降低流体的表面张力。泡沫分散在空气或别的气体介质中。自然界中会有一些泡沫自然生成,例如发酵时。(译者注:原文是alges or fermentation ,但alges怎么也查不到)
水花的性质与之很相似,但不一定需要用tenside(表面活话剂)。海面上,当空气和水混合时,水花很常见。碎浪就是这样。此外分散在空气中的小水珠也能产生spray(译者注:因为这个词有水花,浪花,水雾的意思,为避免误解,后面都用英文。也是为了写脚本命名做好准备)。泡沫和spray不是固定的形态,他们能些微的相互转化。
泡沫和spray白色的外观是光的光谱色散造成的。这个效应就是著名的瑞利散射(Rayleigh scattering)现像:半径比光的波长小很多的微粒对入射光的散射。
这一切与用RealFlow创建spray有什么关系呢? 泡沫的颜色,主要是材质和渲染的事情。此外,物理或化学现像很难实现。但也有一些属性,我们可以运用到模拟泡沫中来
1.创建过程可以基于压力(pressure),年龄(age)或速度(velocity)(碎浪,暴风)
2.Spray可以由小粒子分散在空气中形成。
3. 不同大小的泡沫气泡通常会累积至更大
4.空气分散在水里会导致不同轻重的泡沫,不同于同样密度(dense)的水。
5.泡沫和Spray可以变成水,反之亦然。
6.泡沫和喷雾可以组成大的粒子组
上面已经为我们案例提供很多信息了。 现在我们必须找到一种方法,在 RealFlow 的脚本中实现这些功能。RealFlow的Python脚本涉及到的大部分物理属性,已经在上面列出来了。第三点肯定是最大的问题, 因为它不容易计算粒子之间的内部力量。我们需要用发射器脚本达到这个目标,当然还要有适当的
图15.拍碎浪产生的浪花译者八卦:上面这一大段看起来一点用没有,翻译时很费力差点想放弃这部分。但原作者这么详细的讲泡沫的自然本质有什么用呢。一直到好几个月后我才慢慢理解原作者。这些追求自然本质的精神很值得学习。自然本质才是CG特效的立足点,对我本身影响很大。很感谢原作者。希望也能给正在看的你一些帮助。
正如你看到的,泡沫的创建需要由各种参数来控制: Pressure, velocity, density and age. 次要因素是质量(mass),相邻粒子的数量还有分离的时间。单个的粒子永远不会像泡沫或spray的。
首先我们需要一个粒子发射源。从泡沫粒子中分离出水粒子,建议用第个二发射器来存储产生的粒子。现在第一个挑战就很明确了:声明浪花从水粒子分离泡沫的阈值。最明显的参数肯定是速度(velocity).
Velocity是一个矢量,由x,y,z三个分量组成。但用这三个值来对粒子速度进行检测是不明智的。因为矢量是有一个描述强度的量,就是模长(Magnitude)(如果不了解矢量请看前面章节)。
Vel= (3.2, 2.8, 1.5)
| vel | = sqr(velx2 + vely2 +velz2)
| vel | = sqr(10.24 + 7.84 + 2.25)
| vel | = 4.51
RealFlow提供了一个简单的方法获得模长,不用进行另外的计算:
vel.module( )
这个函数能自动返回给定的矢量的模长。你可以在下面看到脚本是具体怎么使用它的。
def onSimulationStep():
threshold = 2.6
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
particle = water.getFirstParticle()
while (particle):
vel = particle.getVelocity()
vel_mag = vel.module()
pos = particle.getPosition()
id = particle.getId()
if (vel_mag > threshold):
foam.addParticle(pos,vel)
water.removeParticle(id)
particle = particle.getNextParticle()
每个粒子有一个特定的ID。当遍历到这个粒子时,ID可以用来区分,这到底是泡沫粒子(Foam)还是水粒子(Water)。
还要考虑一些事,在设置RealFlow场景时。发射器必须把Resolution值设置为同一大小 。Foam发射器不是用来产生粒子的,而是为了存储浪花粒子的一个容器。所以,Speed值要设置成0.0。water粒子颜色设置成蓝色的,foam粒子设置成白色的,为了更容易区分。
图16: Foam_001设置的场景图17:Foam_001.rfs基于Velocity创建的泡沫
Foam_001.rfs 的脚本语法你已经很熟悉了。这里与前面讨论的是用的同一个思路。
if (vel_mag > threshold):
foam.addParticle(pos,vel)
water.removeParticle(id)
第一句,脚本添加了一个foam发射器,基于当前water 粒子的位移pos和速度vel。第二步,在water发射器中相同的粒子都要被删除。 水和泡沫的分离已经完成。这 些动作只有在速度矢量的模长vel_mag大于threshold值(阈值)才会发生。
看一下上图.扩散的白色泡沫粒子十分均匀。可能有另外参数可以增加随机效果。下一个想法肯定是用Pressure.所以下个脚本,我们比较Pressure的值给一个阈值,代替Velocity.因为Pressure是Float类型,不要再计算模长了。但我们仍需要,速度矢量给addparticle()函数。
def onSimulationStep():
threshold = 10000
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
particle = water.getFirstParticle()
while (particle):
pres = particle.getPressure()
vel = particle.getVelocity()
pos = particle.getPosition()
id = particle.getId()
if (pres > threshold):
foam.addParticle(pos,vel)
water.removeParticle(id)
particle = particle.getNextParticle()
思路是非常相似的,但结果与图17,明显不一样。我们给了一点随机,让它更自然一点。
脚本 Foam_003 和Foam_004用了密度(Density)和质量(Mass),给存在的泡沫。我没有把路径列表放在这里,可以在相关文件夹下找到:
The_Foam_Project > Scripts
替换为
pres = particle.getPressure()
我们用了这些:
dens = particle.getDensity()
mass = particle.getMass()
这里有这些结果的展示:
图19:Foam_002.rfs基于Density创建的泡沫
密度方法也产生很好的模拟与随机扩散的泡沫粒子。这个简单的方法非常有效,也能达到我们要求。与Pressure方法一样成为一个好的备选方案。
不太好的方法当然是使用用质量(mass).所有泡沫和水粒子质量不能随着时间而进行改变。这些所有的测试,都会在后面工作中用到。
图20:Foam_003.rfs基于mass创建的泡沫
现在我们知道,怎样从一个发射器转移粒子到另一个发射器。我们可以把泡沫粒子转化为水粒子。为了这个目的,Age属性就显得很重要,(相比于Velocity和Pressure.)如果观察一个单个的粒子,就会发现Velocity和Pressure是随着时间衰减的。RealFlow模拟这物理效果很准确,所以可以满足我们的目的。
用第三个发射器存储新的粒子是一个好主意。另外颜色可以帮助我们辨别更新中的粒子。为了避免太统一,要加上一个随机因素,可以添加到粒子的Age上。事实上有一种使用模块(Module)的方法。模块是一个外部的延伸,以提高Python的能力。是有很多不同模块的,但RealFlow的最常见的当然是随机模块(random),和(math)数学模块。Random效果显而易见,math提供了一些函数,像正弦(sine),余弦(cosine),平方根(square root),双曲正弦(hyperbolic sine)等。
导入模块语法是:
import module_name
对于随机完整的语句是:
import random
(译者注:这部分是Python基本知识。你稍微了解一下Python就知道Modules)
def onSimulationStep():
import random
threshold_pres = 5000
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
rewater = scene.getEmitter("ReWater")
# Go through water particles
particle_water = water.getFirstParticle()
while (particle_water):
pres_water = particle_water.getPressure()
vel_water = particle_water.getVelocity()
pos_water = particle_water.getPosition()
id_water = particle_water.getId()
if (pres_water > threshold_pres):
foam.addParticle(pos_water,vel_water)
water.removeParticle(id_water)
particle_water = particle_water.getNextParticle()
# Go through foam particles, check age and pressure
particle_foam = foam.getFirstParticle()
while (particle_foam):
current_age = particle_foam.getAge()
random_age = random.uniform(-0.1,0.1)
threshold_age = random_age + 0.5
if (current_age >= threshold_age):
pres_foam = particle_foam.getPressure()
vel_foam = particle_foam.getVelocity()
pos_foam = particle_foam.getPosition()
id_foam = particle_foam.getId()
if (pres_foam < threshold_pres):
rewater.addParticle(pos_foam,vel_foam)
foam.removeParticle(id_foam)
particle_foam = particle_foam.getNextParticle()
图21:Foam_005.rfs 展示了不同状态的结果脚本看着很长,但还是一样的思路。第二个循环仅是把第一个的条件改变一下。我们用泡沫粒子转换成水粒子。为了更真实的动态,我们需要引入Age。
current_age = particle_foam.getAge()
random_age = random.uniform(-0.1,0.1)
threshold_age = random_age + 0.5
唯一新的方法是定义了这个random_age变量。random.uniform(-0.1,0.1)表达式意思是Python创建一个在-0.1到0.1之间的随机数.random_age值是加上了0.5。用这个方法,粒子转变就在0.4到0.6秒之间,但只有在pres_foam值小于5000.
粒子只有达到这两个条件才能转换成水的粒子,他们是半随机的。这个脚本是一个很好的复杂if条件例子。
只要改变几个值,我们就能成功控制大量创建的泡沫。因为脚本已经读出Velocity,我们甚至能增加这些参数,例如,检查不对的Pressure 和Velocity 预渲染。
(本小节未完,请看下面一小节)
评论