away3d传送门特效

away3d 4.0加入特效系统后,做比较炫的特效已经不是什么难事,away3d官方已经基于内置的particle system做了几个挺炫的粒子特效,需要的可以查看github中实例源码。这个传送门特效且作为away3d 粒子系统练手的的另一个实例。

简单说下,这个特效由三个独立的部分组成,转动的符文圆盘、向上发射的半透明光及不断从符文盘向上浮动的颗粒。效果图如下:

Snip201303_3

废话不说,直接上代码解释。

首先,必须准备三个部分的纹理和材质了。分别是符文圆盘、光辉、浮动粒子的纹理,在本教程最后提供的源码里有相应的纹理。

[Embed(source="../assets/pan.png")]
public  var panTexture:Class;

[Embed(source="../assets/light.png")]
public  var lightTexture:Class;

[Embed(source="../assets/blueball.png")]
public  var blueballTexture:Class;

材质拆分成五个部分,分别对应五种不同的粒子mesh。简单说为什么有五种,因为:1圆盘+3光辉+1浮动粒子串。粒子对应的五种material、mesh、animator等的声明如下,它们之间的关系我们将在后面代码解释。

//materials
private var matPan:TextureMaterial;
private var matLight1:TextureMaterial;
private var matLight2:TextureMaterial;
private var matLight3:TextureMaterial;
private var matBall:TextureMaterial;

//particle objects
private var particleAnimationSet1:ParticleAnimationSet;
private var particleAnimationSet2:ParticleAnimationSet;
private var particleAnimationSet3:ParticleAnimationSet;
private var particleAnimationSet4:ParticleAnimationSet;
private var particleAnimationSet5:ParticleAnimationSet;
private var particleGeometry1:ParticleGeometry;
private var particleGeometry2:ParticleGeometry;
private var particleGeometry3:ParticleGeometry;
private var particleGeometry4:ParticleGeometry;
private var particleGeometry5:ParticleGeometry;

// scene objects
private var particleMesh1:Mesh;
private var particleMesh2:Mesh;
private var particleMesh3:Mesh;
private var particleMesh4:Mesh;
private var particleMesh5:Mesh;
private var animator1:ParticleAnimator;
private var animator2:ParticleAnimator;
private var animator3:ParticleAnimator;
private var animator4:ParticleAnimator;
private var animator5:ParticleAnimator;

接着就是初始化引擎了,为了便于理解,采用跟官方一样的方法,顺便贴出代码。

//engine variables
private var scene:Scene3D;
private var camera:Camera3D;
private var view:View3D;
private var cameraController:HoverController;

//signature variables
private var Signature:Sprite;
private var SignatureBitmap:Bitmap;

//navigation variables
private var _move:Boolean = false;
private var _lastPanAngle:Number;
private var _lastTiltAngle:Number;
private var _lastMouseX:Number;
private var _lastMouseY:Number;

/**
 * Initialise the engine
 */
private function initEngine():void
{
	stage.scaleMode = StageScaleMode.NO_SCALE;
	stage.align = StageAlign.TOP_LEFT;

	scene = new Scene3D();
	camera = new Camera3D();

	view = new View3D();
	view.scene = scene;
	view.camera = camera;

	//setup controller to be used on the camera
	cameraController = new HoverController(camera, null, 255, 50, 300, 5);
	view.addSourceURL("srcview/index.html");
	addChild(view);

	//add signature
	Signature = Sprite(new SignatureSwf());
	SignatureBitmap = new Bitmap(new BitmapData(Signature.width, Signature.height, true, 0));
	stage.quality = StageQuality.HIGH;
	SignatureBitmap.bitmapData.draw(Signature);
	stage.quality = StageQuality.LOW;
	addChild(SignatureBitmap);

	addChild(new AwayStats(view));
}

引擎准备完后就是进入主题了,开始制作各种粒子。away3d中的生成粒子有几个步骤:

1、定义粒子需要的几何体集合,比如该教程中传送门的转动圆盘需要1个plane, 浮动的粒子需要50个plane来表现;

2、根据几何体几何,生成粒子需要的ParticleGeometry;

3、根据ParticleGeometry、Material,生成粒子mesh;

4、定义particleAnimationSet。粒子中的动作节点分为局部静态动作、局部动态动作还有全局动作,一个particleAnimationSet可以添加以上多种类型的不同动作节点;进一步了解动作细节,点击查看官方文档

5、根据particleAnimationSet定义ParticleAnimator,并把ParticleAnimator装到粒子mesh上。

6、设置粒子的start time,duration time, delay time及局部动作属性。

文字太抽象,下面是直观的代码:


		/**
		 * 初始化粒子材质
		 */
		private function initMaterials():void
		{
			matPan = new TextureMaterial(Cast.bitmapTexture(panTexture));
			matBall = new TextureMaterial(Cast.bitmapTexture(blueballTexture));
			matLight1 = new TextureMaterial(Cast.bitmapTexture(lightTexture));
			matLight2 = new TextureMaterial(Cast.bitmapTexture(lightTexture));
			matLight3 = new TextureMaterial(Cast.bitmapTexture(lightTexture));

			matPan.bothSides = true;
			matPan.smooth = true;
			matPan.repeat = true;
			matPan.alphaBlending = true;
			matPan.specularColor = 0xFF1161D9;
			matPan.ambientColor = 0xFF1161D9;

			matLight1.bothSides = true;
			matLight1.smooth = true;
			matLight1.alphaBlending = true;

			matLight2.bothSides = true;
			matLight2.smooth = true;
			matLight2.alphaBlending = true;

			matLight3.bothSides = true;
			matLight3.smooth = true;
			matLight3.alphaBlending = true;

			matBall.bothSides = true;
			matBall.repeat = true;
			matBall.smooth = true;
			matBall.alphaBlending = true;
		}

		/**
		 * 初始化粒子
		 */
		private function initParticles():void
		{
			// 设置所有粒子的particleGeometry
			var ball:Geometry = new PlaneGeometry(3,3,1,1,false);
			var light1:Geometry = new CylinderGeometry(50,30,200,16,1,false,false,true);
			var light2:Geometry = new CylinderGeometry(55,30,180,16,1,false,false,true);
			var light3:Geometry = new CylinderGeometry(60,30,160,16,1,false,false,true);
			var pan:Geometry = new PlaneGeometry(65,65,1,1);

			var ballGeoSet:Vector.<Geometry> = new Vector.<Geometry>();
			var light1GeoSet:Vector.<Geometry> = new Vector.<Geometry>();
			var light2GeoSet:Vector.<Geometry> = new Vector.<Geometry>();
			var light3GeoSet:Vector.<Geometry> = new Vector.<Geometry>();
			var panGeoSet:Vector.<Geometry> = new Vector.<Geometry>();

			// particle ball
			for (var i:int = 0; i < 40; i++) {
				ballGeoSet.push(ball);
			}
			particleGeometry1 = ParticleGeometryHelper.generateGeometry(ballGeoSet);

			particleAnimationSet1 = new ParticleAnimationSet(true, true, false);
			particleAnimationSet1.addAnimation(new ParticleBillboardNode());
			particleAnimationSet1.addAnimation(new ParticleVelocityNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,30,0)));
			particleAnimationSet1.addAnimation(new ParticleColorNode(ParticlePropertiesMode.GLOBAL,true,true,false,false,new ColorTransform(), new ColorTransform(1,1,1,0)));
			particleAnimationSet1.addAnimation(new ParticlePositionNode(ParticlePropertiesMode.LOCAL_STATIC));
			particleAnimationSet1.addAnimation(new ParticleOscillatorNode(ParticlePropertiesMode.LOCAL_STATIC));
			particleAnimationSet1.addAnimation(new ParticleVelocityNode(ParticlePropertiesMode.LOCAL_STATIC));
			particleAnimationSet1.initParticleFunc = initBallParticleProperties;

			// light 1
			light1GeoSet.push(light1);
			particleGeometry2 = ParticleGeometryHelper.generateGeometry(light1GeoSet);
			particleAnimationSet2 = new ParticleAnimationSet(true, true, false);
			particleAnimationSet2.addAnimation(new ParticlePositionNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,100,0)));
			particleAnimationSet2.addAnimation(new ParticleRotationalVelocityNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,1,0,25)));
			particleAnimationSet2.addAnimation(new ParticleColorNode(ParticlePropertiesMode.GLOBAL,true,true,false, false,new ColorTransform(1,1,1,0.2), new ColorTransform(1,1,1,0.2)));
			particleAnimationSet2.initParticleFunc = initLightParticleProperties;

			// light 2
			light2GeoSet.push(light2);
			particleGeometry3 = ParticleGeometryHelper.generateGeometry(light2GeoSet);
			particleAnimationSet3 = new ParticleAnimationSet(true, true, false);
			particleAnimationSet3.addAnimation(new ParticlePositionNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,90,0)));
			particleAnimationSet3.addAnimation(new ParticleRotationalVelocityNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,1,0,25)));
			particleAnimationSet3.addAnimation(new ParticleColorNode(ParticlePropertiesMode.GLOBAL,true,true,false, false,new ColorTransform(1,1,1,0.2), new ColorTransform(1,1,1,0.2)));
			particleAnimationSet3.initParticleFunc = initLightParticleProperties;

			// light 3
			light3GeoSet.push(light3);
			particleGeometry4 = ParticleGeometryHelper.generateGeometry(light3GeoSet);
			particleAnimationSet4 = new ParticleAnimationSet(true, true, false);
			particleAnimationSet4.addAnimation(new ParticlePositionNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,90,0)));
			particleAnimationSet4.addAnimation(new ParticleRotationalVelocityNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,1,0,25)));
			particleAnimationSet4.addAnimation(new ParticleColorNode(ParticlePropertiesMode.GLOBAL,true,true,false, false,new ColorTransform(1,1,1,0.2), new ColorTransform(1,1,1,0.2)));
			particleAnimationSet4.initParticleFunc = initLightParticleProperties;

			// pan
			panGeoSet.push(pan);
			particleGeometry5 = ParticleGeometryHelper.generateGeometry(panGeoSet);
			particleAnimationSet5 = new ParticleAnimationSet(true, true, false);
			particleAnimationSet5.addAnimation(new ParticleRotationalVelocityNode(ParticlePropertiesMode.GLOBAL, new Vector3D(0,1,0,8)));
			particleAnimationSet5.initParticleFunc = initPanParticleProperties;

		}

		/**
		 * 初始化场景
		 */
		private function initObjects():void
		{
			particleMesh1 = new Mesh(particleGeometry1, matBall);
			particleMesh2 = new Mesh(particleGeometry2, matLight1);
			particleMesh3 = new Mesh(particleGeometry3, matLight2);
			particleMesh4 = new Mesh(particleGeometry4, matLight3);
			particleMesh5 = new Mesh(particleGeometry5, matPan);
			scene.addChild(particleMesh1);
			scene.addChild(particleMesh2);
			scene.addChild(particleMesh3);
			scene.addChild(particleMesh4);
			scene.addChild(particleMesh5);

			animator1 = new ParticleAnimator(particleAnimationSet1);
			animator2 = new ParticleAnimator(particleAnimationSet2);
			animator3 = new ParticleAnimator(particleAnimationSet3);
			animator4 = new ParticleAnimator(particleAnimationSet4);
			animator5 = new ParticleAnimator(particleAnimationSet5);

			particleMesh1.animator = animator1;
			particleMesh2.animator = animator2;
			particleMesh3.animator = animator3;
			particleMesh4.animator = animator4;
			particleMesh5.animator = animator5;

			animator1.start();
			animator2.start();
			animator3.start();
			animator4.start();
			animator5.start();
		}

		/**
		 * 初始化浮动粒子属性
		 */
		private function initBallParticleProperties(properties:ParticleProperties):void
		{
			properties.startTime = Math.random()*4.1;
			properties.duration = 3;
			var degree1:Number = Math.random() * Math.PI ;
			var degree2:Number = Math.random() * Math.PI;
			var r1:Number = Math.random()*15;
			var r2:Number = Math.random()*20 +10;
			properties[ParticlePositionNode.POSITION_VECTOR3D] = new Vector3D(r1*Math.sin(degree1), 0 , r1*Math.cos(degree1));
			properties[ParticleOscillatorNode.OSCILLATOR_VECTOR3D] = new Vector3D(r2*Math.sin(degree2), 0 , r2*Math.cos(degree2), Math.random()*4+2);
			properties[ParticleVelocityNode.VELOCITY_VECTOR3D] =  new Vector3D(0,Math.random()*100,0);
		}

		/**
		 * 初始化光辉属性
		 */
		private function initLightParticleProperties(properties:ParticleProperties):void
		{
			properties.startTime = 1;
			properties.duration = 10;
		}

		/**
		 * 初始化符文转盘属性
		 */
		private function initPanParticleProperties(properties:ParticleProperties):void
		{
			properties.startTime = 1;
			properties.duration = 1;
		}

猛击下载源码

Away3d粒子系统学习与深入I

最近在做基于away3D引擎的粒子编辑器,因此,有了这部分文章作为mark。

Away3D从4.1alpha之后开始加入了粒子系统功能,再次感谢国内牛人LiaoCheng,是他向away3d贡献的这部分功能,大家鼓掌。

简介

所谓粒子系统是表示三维计算机图形学中模拟一些特定的模糊现象的技术,游戏开发中经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者像发光轨迹这样的抽象视觉效果等等。

Away3D中的粒子系统实现思路

跟大部分游戏的引擎的粒子系统实现思路类似,Away3D的粒子系统遵循以下基本原则:

  1. 每个粒子由带纹理的多边形或几何体构成;
  2. 粒子按一定的规则运动;
  3. 每个粒子有自己的属性,如出生时间、生命周期、位置、速度、加速度、尺寸、颜色、运动轨迹等等;

粒子生成步骤

在Away3D中,由n个粒子组成的粒子特效(为了与广泛的粒子系统区分,我们下面都称为粒子特效)主体,被抽象为一个mesh,而mesh的材质就是给粒子添加的材质,再往mesh中添加animator就组成了一个完整的粒子特效。下面我们就如何构建一个粒子mesh,以及如何为mesh添加动作器展开分析。

构建ParticleGeometry

在上面的的粒子系统基本思路说过:每个粒子由带纹理的多边形或几何体构成,而组成一个完整的粒子特效,则需要n个这样的多边形或几何体,这边我们且称为Geometry Set,我们可以往这个geometry set添加各种Away3D内置的几何体或者外部定义的几何体。出于性能的考虑,建议粒子特效里边最好不要添加太复杂的几何体。

举一个简单的栗子,我们要构建一个向上喷发的小球,就可以如下构建该粒子特效:

var ball:Geometry = new PlaneGeometry(3,3,1,1,false);
var ballGeoSet:Vector.<Geometry> = new Vector.<Geometry>();
// particle ball
for (var i:int = 0; i < 50; i++) {
     ballGeoSet.push(ball);
}

上面的代码,我们添加了包含50个Plane的geometry set,填充这个set可以视情况添加,如果有需要,你也可以同时添加plane、cube、sphere等几何体到这个set里边。准备好geometry set后,我们将使用Away3D提供的particle geometry helper将geometry set转化为最终的ParticleGeometry:

particleGeometry = ParticleGeometryHelper.generateGeometry(ballGeoSet);

这样,我们就完成的一个尚未添加动作的ParticleGeometry实例。

构建ParticleAnimationSet

接下来就是创建粒子动作集合了。类似传统的粒子系统,一个粒子是有其生命周期的,在这个周期中,粒子控制着它所有的属性,位置、轨迹等等。

在Away3D中,一个粒子有关联系统时间的startTime属性,代表粒子出生时间;有可选的duration属性(若没设置duration属性,粒子将无限延续),代表粒子的持续存活时间,还有可选的delay属性(若没设置,将无间隔时间),代表两个重复的粒子周期之间的间隔时间。这些属性基于GPU代码的时间计算,我们必须对要使用的属性进行预定义。

Away3D粒子系统中为粒子动作提供了动作集合ParticleAnimationSet类,构造ParticleAnimationSet需要三个bool参数,第一个参数表示是否使用粒子的duration属性,第二个表示是否不断循环粒子的生命周期,第三个表示是否使用粒子的delay属性,使用则当一个粒子周期执行完成,粒子会消失delay的时间后再进行新的周期。

还是上面的栗子,我们把三个属性都设置为true。

var animationSet:ParticleAnimationSet = new ParticleAnimationSet(true, true, true);
animationSet.initParticleFunc = initParticleParam;

值得注意的是,startTime、duration、delay并不是ParticleAnimationSet的属性,粒子特效中的每个粒子都有自己特有的以上三个属性,在Away3D中,他们被设定为local static属性,我们使用一个函数来定义以上的属性,然后赋给ParticleAnimationSet的initParticleFunc,这样,每个粒子都会调用这个函数一遍。通常,使用ParticleProperties::index这个值设置startTime、duration、delay三个属性,达到每个粒子有不同的以上属性的目的。栗子如下:

private function initParticleParam(prop:ParticleProperties):void {
    prop.startTime = prop.index * 0.005;
    prop.duration = 10;
    prop.delay = 5;
}

以上代码中,我们实现了每5毫秒产生一个粒子,每个粒子延续10秒,然后休眠5秒。

在之后的local static动作属性的设置中,我们也将所用动作的属性设置添加到这个独立的函数中。