埋め込み画像(幅64pixel高さ64pixel)の画素をパーティクルに置き換えて表示してみました。OffsetPositionLocalアクションでランダムな位置に発生させたパーティクルを、BrokenLineLocalアクションで画素位置との2点間を往復移動させて動作がループしているように見せています。Away3Dのバージョンは4.0.9 Goldで、パーティクルシステムは「liaocheng/away3d-particles-system - GitHub」を使用しました。
「Particle Fountain」ではブレンドモードを加算にしていて気が付かなかったのですが、パーティクルが重なったときに奥にあるものが手前に表示されてしまうことがあります。パーティクルシステムの仕様かバグかはわかりません。
package { import flash.display.*; import flash.events.Event; import flash.geom.*; import flash.utils.getTimer; import away3d.containers.View3D; import away3d.debug.AwayStats; import away3d.primitives.PlaneGeometry; import away3d.primitives.WireframePlane; import a3dparticle.ParticlesContainer; import a3dparticle.animators.actions.brokenline.BrokenLineLocal; import a3dparticle.animators.actions.color.RandomColorLocal; import a3dparticle.animators.actions.position.OffsetPositionLocal; import a3dparticle.animators.actions.rotation.BillboardGlobal; 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 ParticleFormation extends Sprite { [Embed(source = "ParticleFormationMaterial.png")] private var materialEmbedClass:Class; [Embed(source = "ParticleFormationPicture.png")] private var pictureEmbedClass:Class; private var img:BitmapData = new pictureEmbedClass().bitmapData; private var view:View3D; private var px:int = 0, py:int = 0; private const PARTICLE_SIZE:int = 15; public function ParticleFormation() { 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); var wireframePlane:WireframePlane = new WireframePlane(2000, 2000, 10, 10, 0xA0A0A0, 0.5, WireframePlane.ORIENTATION_XZ) wireframePlane.y = -PARTICLE_SIZE; view.scene.addChild(wireframePlane); var material:ParticleBitmapMaterial = new ParticleBitmapMaterial(new materialEmbedClass().bitmapData); var plane:PlaneGeometry = new PlaneGeometry(PARTICLE_SIZE, PARTICLE_SIZE, 1, 1, false); var particleSample:ParticleSample = new ParticleSample(plane.subGeometries[0], material); var particle:ParticlesContainer = new ParticlesContainer(); particle.addAction(new BillboardGlobal()); particle.addAction(new OffsetPositionLocal()); particle.addAction(new BrokenLineLocal(3)); particle.addAction(new RandomColorLocal(null, true, false)); particle.initParticleFun = initParticle; particle.playbackSpeed = 0.5; particle.loop = true; particle.generate(new SingleGenerater(particleSample, img.width * img.height)); 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 { var color:uint = img.getPixel(px, py); var r:Number = (color >> 16 & 0xFF) / 0xFF; var g:Number = (color >> 8 & 0xFF) / 0xFF; var b:Number = (color & 0xFF) / 0xFF; var startVector3D:Vector3D = new Vector3D(Math.random() * 2000 - 1000, Math.random() * 1000, Math.random() * 2000 - 1000); var endVector3D:Vector3D = new Vector3D(px * PARTICLE_SIZE - img.width * PARTICLE_SIZE / 2, 0, -py * PARTICLE_SIZE + img.height * PARTICLE_SIZE / 2); var deltaVector3D:Vector3D = endVector3D.subtract(startVector3D); particleParam.startTime = particleParam.index / particleParam.total; particleParam.duringTime = 6; particleParam["OffsetPositionLocal"] = startVector3D; particleParam["BrokenLineLocal"] = [new Vector3D(deltaVector3D.x, deltaVector3D.y, deltaVector3D.z, 1), new Vector3D(0, 0, 0, 2), new Vector3D(-deltaVector3D.x, -deltaVector3D.y, -deltaVector3D.z, 1)]; particleParam["RandomColorLocal"] = new ColorTransform(r, g, b); if (++px == img.width) { px = 0; py++; } } private function enterFrameHandler(event:Event):void { var duration:int = getTimer(); view.camera.x = 1500 * Math.sin(duration / 10000); view.camera.z = -1500 * Math.cos(duration / 10000); view.camera.y = 750 + 250 * Math.sin(duration / 3000); view.camera.lookAt(new Vector3D()); view.render(); } } }