「liaocheng/away3d-particles-system - GitHub」を試してみました。APIリファレンスが無いようなので付属のサンプルを参考に作成しています。Away3Dのバージョンは4.0.9 Goldを使用しました。
UVSeqPicRandomLocalクラスは、パーティクルごとにテクスチャを設定するアクションです。タイル状に並べられたテクスチャを番号で指定して使用します。
package { import flash.display.*; import flash.events.Event; import flash.geom.*; import flash.utils.getTimer; import away3d.containers.View3D; import away3d.core.base.Object3D; import away3d.debug.AwayStats; import away3d.primitives.PlaneGeometry; import away3d.primitives.WireframePlane; import a3dparticle.TransformFollowContainer; import a3dparticle.animators.actions.acceleration.AccelerateGlobal; import a3dparticle.animators.actions.color.ChangeColorByLifeGlobal; import a3dparticle.animators.actions.rotation.BillboardGlobal; import a3dparticle.animators.actions.velocity.VelocityLocal; import a3dparticle.generater.SingleGenerater; import a3dparticle.particle.ParticleBitmapMaterial; import a3dparticle.particle.ParticleParam; import a3dparticle.particle.ParticleSample; [SWF(width = "480", height = "270", frameRate = "60")] public class ParticleFountain extends Sprite { [Embed(source = "ParticleFountain.png")] private var ImgCls:Class; private var view:View3D; private var targetObject:Object3D = new Object3D(); public function ParticleFountain() { if (stage) addedToStageHanlder(); else addEventListener(Event.ADDED_TO_STAGE, addedToStageHanlder); } private function addedToStageHanlder(event:Event = null):void { view = new View3D(); view.antiAlias = 4; addChild(view); view.scene.addChild(new WireframePlane(2000, 2000, 10, 10, 0xA0A0A0, 0.5, WireframePlane.ORIENTATION_XZ)); var material:ParticleBitmapMaterial = new ParticleBitmapMaterial(new ImgCls().bitmapData); material.blendMode = BlendMode.ADD; var plane:PlaneGeometry = new PlaneGeometry(18, 18, 1, 1, false); var particleSample:ParticleSample = new ParticleSample(plane.subGeometries[0], material); var particle:TransformFollowContainer = new TransformFollowContainer(); particle.addAction(new BillboardGlobal()); particle.addAction(new UVSeqPicRandomLocal(2, 2)); particle.addAction(new VelocityLocal()); particle.addAction(new AccelerateGlobal(new Vector3D(0, -500, 0))); particle.addAction(new ChangeColorByLifeGlobal(new ColorTransform(1, 0.5, 0.5), new ColorTransform(0.5, 0.5, 1))); particle.initParticleFun = initParticle; particle.followTarget = targetObject; particle.loop = true; particle.generate(new SingleGenerater(particleSample, 4096)); particle.start(); view.scene.addChild(particle); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; addChild(new AwayStats(view)); addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function initParticle(particleParam:ParticleParam):void { particleParam.startTime = Math.random() * 3; particleParam.duringTime = 2.8; var r:Number = 700; var degree1:Number = Math.random() * Math.PI * 2; var degree2:Number = 80 * Math.PI / 180 + Math.random() * 40 * Math.PI / 180; particleParam["UVSeqPicRandomLocal"] = particleParam.index % 4; particleParam["VelocityLocal"] = new Vector3D(r * Math.sin(degree1) * Math.cos(degree2), r * Math.sin(degree2), r * Math.cos(degree1) * Math.cos(degree2)); } private function enterFrameHandler(event:Event):void { var duration:int = getTimer(); targetObject.x = 1000 * Math.cos(duration / 800); targetObject.z = 1000 * Math.sin(duration / 400); view.camera.x = 1500 * Math.sin(-duration / 10000); view.camera.z = 1500 * Math.cos(-duration / 10000); view.camera.y = 500 + 250 * Math.sin(duration / 3000); view.camera.lookAt(new Vector3D()); view.render(); } } }
package { import flash.display3D.Context3DVertexBufferFormat; import flash.geom.Vector3D; import away3d.core.base.IRenderable; import away3d.core.managers.Stage3DProxy; import away3d.materials.passes.MaterialPassBase; import away3d.materials.utils.ShaderRegisterElement; import a3dparticle.animators.actions.PerParticleAction; import a3dparticle.animators.ParticleAnimation; import a3dparticle.core.SubContainer; import a3dparticle.particle.ParticleParam; public class UVSeqPicRandomLocal extends PerParticleAction { private var _columns:int; private var _rows:int; private var _fun:Function; private var _temp:int; private var _uvParamAttrubite:ShaderRegisterElement; public function UVSeqPicRandomLocal(columns:int, rows:int, fun:Function = null) { priority = ParticleAnimation.POST_PRIORITY + 5; dataLenght = 4; _name = "UVSeqPicRandomLocal"; _columns = columns; _rows = rows; _fun = fun; } override public function set animation(value:ParticleAnimation):void { super.animation = value; value.hasUVAction = true; } override public function genOne(param:ParticleParam):void { if (_fun != null) { _temp = _fun(param); } else { _temp = param[_name]; } } override public function distributeOne(index:int, verticeIndex:uint, subContainer:SubContainer):void { getExtraData(subContainer).push(1 / _columns, 1 / _rows, _temp % _columns / _columns, Math.floor(_temp / _columns) / _rows); } override public function getAGALVertexCode(pass:MaterialPassBase):String { var code:String = ""; if (_animation.needUV) { _uvParamAttrubite = shaderRegisterCache.getFreeVertexAttribute(); var uvScale:ShaderRegisterElement = new ShaderRegisterElement(_uvParamAttrubite.regName, _uvParamAttrubite.index, "xy"); var uvOffset:ShaderRegisterElement = new ShaderRegisterElement(_uvParamAttrubite.regName, _uvParamAttrubite.index, "zw"); var uv:ShaderRegisterElement = new ShaderRegisterElement(_animation.uvTarget.regName, _animation.uvTarget.index, "xy"); code += "mul " + uv.toString() + "," + uv.toString() + "," + uvScale.toString() + "\n"; code += "add " + uv.toString() + "," + uv.toString() + "," + uvOffset.toString() + "\n"; } return code; } override public function setRenderState(stage3DProxy:Stage3DProxy, renderable:IRenderable):void { if (_animation.needUV) { stage3DProxy.setSimpleVertexBuffer(_uvParamAttrubite.index, getExtraBuffer(stage3DProxy, SubContainer(renderable)), Context3DVertexBufferFormat.FLOAT_4, 0); } } } }