分享
 
 
 

三维粒子系统在FlashAS2中的实现

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

摘要:

本文通过粒子系统及三维粒子透视投影变换,运用FlashAS2及其开发环境,实现了基本的三维粒子系统。并结合相关的曲线及运动方式,实现了抛体运动,Fermat螺线上升运动以及龙卷风效果。表现了一定的数字艺术层的美感。

关键词:粒子系统,三维,数字艺术

1前言:

三维粒子系统是一类令人感到激动又十分有趣的动画程序。它的实现方式主要需要用基于粒子系统构建的图形学,动力学以及数字艺术等多方面的知识。[1]介绍了基本的三维视图通过透视投影变换到二维场景的方式。[2]介绍了一个基本的粒子系统的实现。在这两篇文章的基础上,再结合比较简单的运动学方面的知识,在本文中实现了基本的三维粒子系统,并实现了抛体运动,Fermat螺线上升运动以及龙卷风效果。这些作品表现了一定的数字艺术层的美感。

另外,开发选用的是基于AS2语言的Flash开发平台,这种开发模式具有以下优点:

1)Flash播放器具有极高的普及率,而且swf文件格式是跨平台的。

2)Flash的失量图形处理模式极强,适合网络传播图像,动画及各类互动效果。

3)Flash平台下的交互动画开发方便,采用了AS2语言可以面向对象的模式来组织程序结构。交互效果可以实现的非常到位。可以说,是一种面像图形开发的脚本语言。(正如Matlab可以说是面向科学工程计算的脚本语言一样。)

当然,这种模式也是有缺点的,比如Flash播放器在处理一些较大运算时的效果不是很

理想。

由于这个程序重点在实现相应的图形效果上,主要是测试之用,所以采用Flash的AS2

来进行是非常合适的。

2程序总体思路及关键部分:

2.1粒子系统的框架:

[2]中已有叙述,这里简要回顾之:

Initialization

while(runFlag)

{

Forallparticles

{

If(currentparticleisnotlived)

{

Initthisparticle.

}

Elseif(currentparticleisoutoftheshowingarea)

{

Currentparticlesettodead.

}

}

}

2.2三维粒子在二维场景的透视成像:

[1]中已有详述,这里简要回顾之:

对于一个在三维场景中的点p(x,y,z)

其在距离原点距离为d处的位于z轴正半轴上的点(0,0,d)而言,其对应到二维投影面的点p’(x’,y’)有如下计算公式:

x’=x/(-z/d+1)

y’=y/(-z/d+1)

图1

2.3.1抛体运动模式关键:

抛体运动在这里的右手坐标系(图1)中,是指以y为竖直运方向作上抛运动,x,z方向以固定速度运动的方式。我们在公园里看到的许多喷泉就是这样的例子。

由于初始化后所有粒子统一生成,所以要看到类似喷泉的效果,需要待几个粒子生成周期之后,生成粒子与死亡粒子可以持续交替时,便会呈现出不错的效果。图2展示了300粒子在程序运行过几个粒子生成周期后的效果。

相应初始程序、参数及运动函数如下:

//dataset

tmp=Math.random();

_root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0);

_root.particleArr[i].initPos(gX0-10+random(20),gY0-50+random(150),gZ0-10+random(20));

//motioneffectmode

for(vari:Number=0;i<_root.gParticleNum;i++)

{

if(_root.particleArr[i].isLived())

{

_root.particleArr[i].moveCal();

_root.particleArr[i].moveShow();

}

}

图2

2.3.2Fermat螺线上升运动:

Fermat螺线的极坐标表达为:

ρ=a*θ^(1/2)

这里要做的,就是将θ角在某粒子与xz平面垂直的截平面上对应的Fermat螺线极坐标转换成x及z坐标值。再进行相应的更新即可。与前一种不同的是,这里只有y轴方向需要用到vy,而x,z轴的位置则由于采用了坐标控制模式,所以不需要使用vx,vz。为了整体程序设计上的统一,可将vx,vz设定为0。

与上一个抛物线型运动不同,这里的Fermat螺线运动如果新生粒子与死亡粒子构成了持续生成关系时,将使画面较为混乱。因此,需要将粒子的初始y值及vy,ay设定的靠近些。

可以看到,团状的粒子簇旋转上升后又扩散下降,无序之中体现着一丝韵律,我将这样的效果命名为“银河落九天”,不知诸位为以为如何?(参图3)

//dataset

tmp=Math.random();

_root.particleArr[i].initV_A(0,18+random(2),0,0,-0.5+0.1*Math.random(),0);

_root.particleArr[i].initRotateEle(0,0,2*Math.PI*i/_root.gParticleNum);

_root.particleArr[i].initPos(gX0-15+random(30),gY0-5+random(10),gZ0-15+random(30));

_root.particleArr[i].fermatSpiralA=15+random(10);

//motioneffectmode

for(vari:Number=0;i<_root.gParticleNum;i++)

{

if(_root.particleArr[i].isLived())

{

_root.particleArr[i].accTheta(Math.PI/18);

_root.particleArr[i].moveCalFermatSpiralWithY();

_root.particleArr[i].moveShow();

}

}

图3

2.3.3龙卷风效果:

龙卷风效果,这里没有采用从坐标原点生成的模式,而是直接摸拟了生成后运动的效果,难度降低了不小。

可以这样近似看待龙卷风的模拟,每个粒子都以y轴为中心,以不同的运动半径作圆周运动。各粒子的运动半径从下往上递增。

以上面的设计理念,便可生成一种倒锥型的龙卷风,如图4。显然,在这种模式中,没有粒子会死亡。

//dataset

_root.particleArr[i].initV_A((0.5*Math.random())*Math.cos(2*Math.PI*tmp),0,(0.5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0);

_root.particleArr[i].initRotateEle(0,0,2*Math.PI*Math.random());

_root.particleArr[i].initPos(gX0+(i+1)*2-2+random(4),gY0+50-baconHeight/_root.gParticleNum*i-random(50),gZ0+(i+1)*2-2+random(4));

baconHeight=400

//motioneffectmode

for(vari:Number=0;i<_root.gParticleNum;i++)

{

if(_root.particleArr[i].isLived())

{

_root.particleArr[i].accTheta(Math.PI/18);

_root.particleArr[i].moveWithY();

_root.particleArr[i].moveShow();

}

}

图4

如果再对粒子生成加以改进,即位于龙卷风高度的75%以下的部分主要生成一个类似柱状的效果,而对于龙卷风高度的75%以上部分则扩散出去,形成一个更为逼真的效果。(如图5)

1控制生成高度及速度:

if(i<0.7*_root.gParticleNum)

{

_root.particleArr[i].initV_A((5*Math.random())*Math.cos(2*Math.PI*tmp),0,(5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0);

_root.particleArr[i].initPos(gX0-50+random(100),gY0+50-baconHeight/_root.gParticleNum*i-random(50),gZ0-50+random(100));

}

else

{

_root.particleArr[i].initV_A((5*Math.random())*Math.cos(2*Math.PI*tmp),0,(5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0);

if(random(100)<50)

_root.particleArr[i].initPos(gX0+100+random(100),gY0+25-baconHeight/_root.gParticleNum*i-random(25),gZ0+100+random(100));

else

_root.particleArr[i].initPos(gX0-100-random(100),gY0+25-baconHeight/_root.gParticleNum*i-random(25),gZ0-100-random(100));

}

2保持上下不同的旋转速度:

if(i<0.7*_root.particleNum)

_root.particleArr[i].accTheta(Math.PI/24+0.05*Math.random());

else

_root.particleArr[i].accTheta(Math.PI/18+0.1*Math.random());

图5(1000粒)

3关键代码:

3.1粒子类

importE3DPack.*;

importSColDet.*;

importSMotion.*;

classE3DPack.E3DPhyNodeextendsE3DNode

{

//--locationpropertiesextends.--

/*

publicvarx:Number;

publicvary:Number;

publicvarz:Number;

*/

publicvarbufferX:Number=0;//fortheclear,re-drawmodehere.

publicvarbufferY:Number=0;

publicvarbufferZ:Number=0;

//--dynamicproperties--

publicvarm:Number=0;//mass

publicvarg:Number=0;//gravity

publicvarpF:Number=0;//Positiveforces,attentionhereUpCase!!!!!!!

//Becausethecompilerwasnotsoperfectasyouthink,addapheretoprepareforthecase.

publicvarr:Number=0;//whenitbecomeaball---radius.

publicvarv:Number=0;//1demisionVelocityortogetherVelocityofvx,vy

publicvarvx:Number=0;

publicvarvy:Number=0;

publicvarvz:Number=0;

publicvarf:Number=0;//fractionforces.

publicvarfx:Number=0;

publicvarfy:Number=0;

publicvarfz:Number=0;

publicvara:Number=0;//accleratev

publicvarax:Number=0;

publicvaray:Number=0;

publicvaraz:Number=0;

publicvarfN:Number=0;

publicvaraN:Number=0;

publicvartheta:Number=0;

publicvaraNXZ:Number=0;

//theacclerationvalueforrotatewithy-axis

publicvarrXZ:Number=0;

//theradiusinrotatewithyaxis.

//forfermatspiralsonly

varfermatSpiralA:Number=5;

//--lifepropertiesofparticles--

privatestaticvarDEAD:Number=0;

privatestaticvarLIVED:Number=1;

privatevarlife:Number;

privatestaticvarthisP:Object;

//--ultioperationcomponent--

privatevarmMotionCom:RCSMove;

privatevarmColDetCom:RCSColDet;

//--functionwhichisusefulbyextends.--

/*

publicfunctionE3DNode(inX:Number,inY:Number,inZ:Number)

publicfunctionresetXYZ(inX:Number,inY:Number,inZ:Number)

publicfunctiongetPerspective(viewDistance:Number):Number

publicfunctiontransTo2DNode(viewDistance:Number):E2DNode

publicfunctiontransTo2DNode2(projectPos:Number):E2DNode

publicfunctionrotateAroundZ(fi:Number):Void

publicfunctionrotateAroundX(fi:Number):Void

publicfunctionrotateAroundY(fi:Number):Void

*/

//--selffunction--

publicfunctionE3DPhyNode(inX:Number,inY:Number,inZ:Number)

{

x=inX;

y=inY;

z=inZ;

}

publicfunctionsetLife(lifeValue:Number):Void

{

life=lifeValue;

}

publicfunctiongetLife():Number

{

returnlife;

}

publicfunctionisLived():Boolean

{

returnlife==LIVED;

}

//-initializationfunctions-

publicfunctioninitThisPtr():Void

{

thisP=this;

}

publicfunctioninitCom():Void

{

mMotionCom=newRCSMove();

mColDetCom=newRCSColDet();

}

publicfunctioninitV_A(inVx:Number,inVy:Number,inVz:Number,inAx:Number,inAy:Number,inAz:Number):Void

{

this.vx=inVx;

this.vy=inVy;

this.vz=inVz;

this.ax=inAx;

this.ay=inAy;

this.az=inAz;

}

publicfunctioninitPos(inX:Number,inY:Number,inZ:Number):Void

{

this.x=inX;

this.y=inY;

this.z=inZ;

this.bufferX=this.x;

this.bufferY=this.y;

this.bufferZ=this.z;

}

publicfunctioninitRotateEle(inFn:Number,inAn:Number,inTheta:Number):Void

{

this.fN=inFn;

this.aN=inAn;

this.theta=inTheta;

}

//--moveandshowfunctions--

publicfunctionmoveCal():Void

{

this.vx+=this.ax;

this.vy+=this.ay;

this.vz+=this.az;

this.bufferX+=this.vx;

this.bufferY-=this.vy;

this.bufferZ+=this.vz;

}

publicfunctionmoveShow():Void

{

vartmp2DNode=newE2DNode(0,0);

/*globalvarialbedepends:

gNodeColor,gNodeTransparent,gOffsetX,gOffsetY

*/

//1.cleartheoldnode

//inauplever,globalclearmode.

//2.updatetheposition,actually,thereisnoneedtousebufferx,y,zhereinflash.

//butthedesignhereseemtobemoreflexible

this.x=this.bufferX;

this.y=this.bufferY;

this.z=this.bufferZ;

//3.redraw.

tmp2DNode=this.transTo2DNode2(_root.gZProjectDis);

_root.gBrush.moveTo(_root.gOffsetX+Number(tmp2DNode.x)-1,_root.gOffsetY+Number(tmp2DNode.y)+1);

_root.gBrush.lineTo(_root.gOffsetX+Number(tmp2DNode.x)+1,_root.gOffsetY+Number(tmp2DNode.y)-1);

}

//--checkfunctions--

publicfunctionoutDetect():Boolean

{

if(this.y>400)

returntrue;

else

returnfalse;

}

//--otherultimovingfunctions--

publicfunctionaccTheta(detaAng:Number):Void

{

this.theta+=detaAng;

}

publicfunctioncalANXZ():Void

{

this.aNXZ=(this.vx*this.vx+this.vz*this.vz)/Math.sqrt(this.x*this.x+this.z*this.z);

}

publicfunctioncalRXZ():Void

{

this.rXZ=Math.sqrt(this.x*this.x+this.z*this.z);

}

publicfunctioncalAxAzInRotate():Void

{

this.aN=(this.vx*this.vx+this.vz*this.vz)/Math.sqrt(this.x*this.x+this.z*this.z);

this.ax=this.aN*Math.cos(this.theta);

this.az=this.aN*Math.sin(this.theta);

}

publicfunctioncalAxAzInRotate2():Void

{

this.ax=this.aNXZ*Math.cos(this.theta);

&nb,sp;

this.az=this.aNXZ*Math.sin(this.theta);

}

publicfunctionmoveWithY():Void

{

this.bufferX=this.rXZ*Math.cos(this.theta);

this.bufferZ=this.rXZ*Math.sin(this.theta);

}

publicfunctionmoveCalFermatSpiralWithY():Void

{

this.vy+=this.ay;

this.bufferX=this.fermatSpiralA*Math.sqrt(this.theta)*Math.cos(this.theta);

this.bufferY-=this.vy;

this.bufferZ=this.fermatSpiralA*Math.sqrt(this.theta)*Math.sin(this.theta);

}

}

3.2初始化函数(以抛物线运动作为示例)

functioninitParticles():Void

{

for(vari:Number=0;i<_root.gParticleNum;i++)

{

vartmp:Number=0;

vartmpVr:Number=0;

_root.particleArr[i].setLife(LIVED);

_root.particleArr[i].initThisPtr();

_root.particleArr[i].initCom();

tmp=Math.random();

//_root.initRotateEle(2,2,0);

_root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0);

_root.particleArr[i].initPos(gX0-10+random(20),gY0-50+random(150),gZ0-10+random(20));

}

}

3.3主循环函数(以抛物线运动为基础)

functionmainLoop():Void

{

vartmp:Number=0;

if(_root.gRunFlag)//ainfiniteloopingsystem.

{

//--livecheckorregenerateparticle--

for(vari:Number=0;i<_root.gParticleNum;i++)

{

if(!_root.particleArr[i].isLived())

{

_root.particleArr[i].setLife(LIVED);

tmp=Math.random();

_root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0);

_root.particleArr[i].initPos(gX0-10+random(20),gY0+random(50),gZ0-10+random(20));

}

elseif(_root.particleArr[i].outDetect())

{

_root.particleArr[i].setLife(DEAD);

}

}

//--particlesupdatingandredraw.--

//--lastpicclear.--

_root.gBrush.clear();

//--newbrushset--

_root.gBrush.lineStyle(1,_root.gNodeColor,_root.gNodeTransparent);

for(vari:Number=0;i<_root.gParticleNum;i++)

{

if(_root.particleArr[i].isLived())

{

_root.particleArr[i].moveCal();

_root.particleArr[i].moveShow();

}

}

}

}

4实验结论:

本文论述了3D粒子系统的基本框架并实现了相应的运动效果,如抛体,龙卷风运动等,具有一定的参考意义。并且,对于基于更高级物理效果的运动模拟,如絮流等,作好了准备工作。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有