动态利萨如图形(LissajousFigures)利萨如图形(Lissajousfigures,也可以叫利萨如曲线)有一个相关物理的背景,就是用来理解交流电(alternatingcurrents)。这个曲线是是两个沿着互相垂直方向的正弦振动的合成的轨迹。可以使用“点”在2D/3D空间绘制出利萨如图形。“点”的位置由时间控制,就是说需要进行解算,可以用批处理脚本(batchscript)模拟这一过程。本文方法可以加速此图形生成,直到生成最终你需要的结果才会输出,而不是每一帧输出一个BIN文件。如果每帧生成可能会产生成百上千个文件。维基百科定义:(有中文的简要词条,注意切换)本教程提供的的脚本是完美的解决方案:基于时间生成,使用不同的设置,会产生有趣的效果。LissajousFigures可探讨范围几乎是无限的,因为时间没有边界和极限的。Lissajousfigure可以在示波器上看到,振荡曲线,钟摆曲线,甚至绘制圆柱体。如果提前想提前感受一下绘制利萨如图形的趣味,可以到这个网站:本教程,提供了一个如何以批处理方法创造很炫的效果。请注意这是为RealFlow5写的脚本,对于老版本只要进行一下小小改动就可以了。上图是动态的绘制过程最重要的任务是找到创建LissajousFigures的合适函数。你可以在网上随便搜索一下,会发现很多网页有这个公式。适当的表达式是这样的:上图为维基百科的参数方程截图t-Ax*sin(ω1*t+ρ1)t-Ay*sin(ω2*t+ρ2)但写成上面这样,容易转换成脚本。可以从公式看出绘制出的图形与时间t紧密相关:每个时间步长t,脚本计算公式A*sin(ω*t+ρ),这在每个轴都会计算。正如你看到的,这个公式只考虑x和y轴方向。而我们目的是绘制三维图。解决方法是很简单的,因为只要把这个公式加一项,就能求得Z轴:t-Az*sin(ω3*t+ρ3)决定最后图形尺寸大小的值是A,A越大结构就变得越大下一步是找出ω和ρ的意思。他们读作“Omega(o`miga欧米伽)”和“Phi(佛爱φ,应该写成这样)”他们决定图的样子。ω是频率,ρ是相位(phase)。因为在这里要处理震荡(oscillation),这两个参数是必须的的。频率是指在一定时间内(=向上/向下运动)发生的数量。越高的频率,就会有更多的波峰和波谷。(这些就是简单的正弦曲线传达的概念这篇文章有一段,详细介绍了频率相位这些概念)图1.在二维空间不同的Lissajousfigure示例。相位,ρ,不太容易解释。根据定义,它是振荡状态随时间变化与起始值的距离。简明的说来,就是Lissajousfigure整体外观的偏移。依据时间的批处理脚本(BatchScripts)使用模拟事件(simulation),我们不会碰到任何关于时间(time)的问题,因为每个模拟步长或帧,可看作时间的流逝。在批处理脚本中不会这么自动,因为这个脚本类型不是基于任何模拟或事件。因此,时间可被考虑成一个无限的序列,在每一步添加一定值。在计算机模拟中,甚至可以用负的时间值。但关键是,使用负时间真的能产生一些新的结果或图形吗?实际上可以创建一个循环,让计数器在每一步加1。粒子数也需要循环来定义,你最后就可以控制图片的“分辨率”了(一张图形,有运行时间越长,粒子数越就可能会越清晰。这是一种比拟)。正如您所了解到,从ρ定义你需要一些初始值。这是必要的,因为新的时间步是基于之前那个的。这个思路来自于分形(fractals)(详细了解分形请参考文章:分形艺术)。你需要让时间初始值要大于或等于0,因为没有“负”的时间。time=0step=0.025foriinrange(0,2000,1):time=time+step#用这三个公式计算单个值#创建位移矢量#添加粒子发射器核心部分是:foriinrange(0,2000,1)这个行命令意思是:“变量i要从1运行到2000,步长是1。”结果是一个整数序列:1,2,3,4,…2000。在这个循环每一个“i”常量值是0.025(=step(步长)),会添加到当前时间。最初时间是0,现在增加每一个步长在这个循环中:0.025,0.050,0.075,...,50.这基本上是Axyz,ω123,ρ123起始值。在循环之前,要先引入这些变量。现在我们准备好了所有需要的批处理脚本。最初的参数代表最终图片外形。当成一种游戏来玩吧,你会惊讶的发现即使只是轻微的改变,就创建出完全不同的图形。importmathscene.reset()Ax=2.0Ay=2.0Az=2.0omega1=1.67omega2=1.22omega3=0.89phi1=1.54phi2=2.98phi3=1.68time=0step=0.025stop=2000nullVel=Vector.new(0,0,0)emitter=scene.get_PB_Emitter(Container01)emitter.setParameter(Type,Dumb)foriinrange(0,stop,1):time=time+stepx=Ax*math.sin(omega1*time+phi1)y=Ay*math.sin(omega2*time+phi2)z=Az*math.sin(omega3*time+phi3)particlePos=Vector.new(x,y,z)emitter.addParticle(particlePos,nullVel)使用这个脚本步骤:1.建立一个Container粒子发射器(默认名称Container01)2.在Layout菜单下,打开BatchScript窗口。3.粘贴入上面提供的脚本4.在这个窗口下的Script下点击Run5.会看RealFlow开始自动绘制曲线。成功!!!注意这时你只能看到粒子,它是没有变成Bin文件导出的,你拨一下时间栏它就可能消失。可以点击一下Simulation,就会保存下来了你可以导出这个粒子渲染出去,加一些后期修饰什么的。本文章开始说,为了避免产生N多个粒子文件,所以在BatchScript下写这个脚本。像如果想要动态序列粒子怎么办??自己小小思考下。再往下看吧。到时你可以比较一下,他的方法好还是你的方法好。在开始使用这个脚本前插播一个关于Container小介绍Tips:Container(容器)这也是一种发射器类型,类似于binary和NBinary发射器类型。实际上它不是真的发射粒子,因为它本身只作为一种接受其实粒子源的容器,只接受,不产生。通常这种类型与FilterDaemon(这是一种过虑辅助器)结合使用。你可以指定特写条件下的粒子转移到这个Container发射器中。这就很容易制作泡沫(foam)或水花(spry)了,会单独输出来。尽管这个发射器不发射粒子,它具有所有其它发射器具有的功能。它的粒子可以与刚体(rigidbody)或RealWave进行交互,你也可以控制所有相关的物理参数。这个Container粒子容器自身的面板,不提供任何设置,因为它只能从其实粒子发射器接受粒子。当然,循环终止值可以小于或大于2000。另一个重要的事是要使用dumb粒子。为避免麻烦,脚本执行这必须做的事。另外粒子velocity是0.如果你想保存这图形,只要保存场景或创建初始状态。用高一点Resolution(译者注:经本人试验,这个值与最终粒子数无关,最终粒子数等于stop值),用稠密的粒子拖尾,图形也可以mesh.最好用不同ω和ρ值进行试验创建Lissajousfigures,这两值对最终影响很大。问题是,要在哪里使用到这个脚本呢?实际上它是个噱头,实际工作中流体或粒子模拟这些图片不会发挥非常重要的作用。这个示例告诉你怎样转变公式用到Python代码中,你还得到一个有趣的结果。并学习到关于物理和数学上的一些东西,物理和数学对我们CG工作者是非常重要的。这个特殊的批处理脚本方法更多的优点是:你能轻松在三维软件中可视化物理过程和结构。你可以专注于研究脚本并不用太意于投射(projection),透视(perspective),材质(shading),粒子系统(particlesystem)等。(这是一种很重要的认识,可以把工作试验当成一种很有趣的事来做,并不需要能得到什么了不起的结果,享受这种把物理或数学变成图形的过程吧。况且最终的它真的对工作没有帮助吗?这个答案由来你回答了。)动态化利萨如图形(LissajousFigures)上面介绍的是批处理脚本的一部分,粒子位置可以绘制出来了。你肯定注意到生成图形时的动画也是很有趣的。这代码与之前脚本几乎完全一样的,但需要一个函数创建每一帧的变化。可以使用参数是“omega”和“phi”.最好办法是组合一个基于帧的值。函数输出当前帧是:frame=scene.getCurrentFrame()再让“omega”和“phi”参数乘以当前帧。但仅这样处理图形会没有平滑过渡,因为越来越多帧,“omega”,“phi”的值将会越来越高。为了获得更好的效果,首次测试值除以200。omega1-3=initialValue*frame/200phi1-3=initialValue*frame/200当然你也可以换成其它值,例如100或300。再给“omega”和“phi”设置不同的值。用更小的值最终过渡会更光滑。这代码放在“StepsPost”事件部分。目前场景模拟不会有任何合理图形,因为每一帧新的粒子会添加到之前的粒子上,所有看起来很糟糕。现在需要什么方法更新这个场景给每一个新帧。RealFlow提供了“reset()”函数,但不能用在这儿,因为这只会跳回初始帧。方法是放在FramesPre里写上第二个脚本。现在在StepsPost里的核心脚本生成新粒子之前的粒子会被删除。这个程序只有几行,但非常有效:emitter=scene.getEmitter(Container01)particleList=emitter.getParticles()forparticleinparticleList:emitter.removeParticle(particle.getId())这已经是整个脚本来刷新屏幕。利用RealFlow的拍屏功能你可以轻松看到模拟的动画效果。这一部分是放在StepsPre下的脚本(Part1):emitter=scene.getEmitter(Container01)particleList=emitter.getParticles()forparticleinparticleList:emitter.removeParticle(particle.getId())第二部分是放在StepsPost下面importmathframe=scene.currentFrame#scene.reset()Ax=2.0Ay=2.0Az=2.0omega1=1.67*frame/200omega2=1.22*frame/200omega3=0.89*frame/200phi1=1.54*frame/200phi2=2.98*frame/200phi3=1.68*frame/200time=0step=0.025stop=2000nullVel=Vector.new(0,0,0)emitter=scene.get_PB_Emitter(Container01)emitter.setParameter(Type,Dumb)foriinrange(0,st