While working on a little Valentine’s Day ecard for the most wonderful wife in the world, I suddenly had an idea…
Snow effects have been done in Flash since at least Flash 5 (maybe even Flash 4 if I think back hard enough), but I have yet to see a Flash snow storm in “true 3d”. Well, at least not until now. Seemed the new Particle API in Papervision3d was just the way to go for such a thing. Probably one of the oldest and nicest looking examples of snow in Flash, is the one presented on Kirupa.com. I really like the motion of that version, so borrowed a bit of the math for my own updated version. Also, I like the idea of using depth of field with papervision, so whilst borrowing, I boosted a bit of mrdoob’s “brute force” approach for depth of field. I didn’t go crazy with 200 textures though. I figured 4 were enough. No blur, a light blur, a medium blur, and a heavy blur. To be honest, you can’t really even notice the DOF anyway, but it’s there, and that’s all that matters. Add a little panorama and suddenly it’s snowing everywhere you look. Now, if I could just figure out how to keep the flakes from “sticking” to the camera, I’d have it made in the shade. I’m not sure if this is a clipping problem or what. If anyone knows why that happens, post a comment here and let me know.
The code for some snow:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
/** * Snow in the Third Dimension (with some math from Kirupa.com) * @author Devon O. * @version 0.1 */ package { import flash.display.BitmapData; import flash.display.GradientType; import flash.display.Sprite; import flash.filters.BlurFilter; import flash.geom.Matrix; import flash.geom.Point; import org.papervision3d.cameras.FreeCamera3D; import org.papervision3d.materials.special.BitmapParticleMaterial; import org.papervision3d.view.BasicView; import flash.events.Event; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.core.geom.Particles; import org.papervision3d.core.geom.renderables.Particle; public class Snow3d extends BasicView { private var snowHolder:DisplayObject3D; private var flakes:Array; private const NUM_FLAKES:int = 300; private var cam:FreeCamera3D; private var noBlurFlake:BitmapData; private var ltBlurFlake:BitmapData; private var medBlurFlake:BitmapData; private var heavyBlurFlake:BitmapData; public function Snow3d() { super(stage.stageWidth, stage.stageHeight, true, false, FreeCamera3D.TYPE); init(); } private function init():void { cam = cameraAsFreeCamera3D; cam.zoom = 1; cam.focus = 400; cam.z = 10; createBitmaps(); snowHolder = new DisplayObject3D("snow_holder"); flakes = new Array(); for(var i:int = 0; i < NUM_FLAKES; i++) { var mat:BitmapParticleMaterial = new BitmapParticleMaterial(noBlurFlake); mat.smooth = true; var flakeHolder:Particles = new Particles("flake" + i); var flake:Particle = new Particle(mat, 10, 0, 0, 0); flakeHolder.addParticle(flake); flakeHolder.x = randRange(-2000, 2000); flakeHolder.y = randRange(-2000, 2000); flakeHolder.z = randRange( -2000, 2000); flakeHolder.extra = { radians: 0, I:randRange(2, 8), K: (-Math.PI + Math.random() * Math.PI), noblur: new BitmapParticleMaterial(noBlurFlake), blur1: new BitmapParticleMaterial(ltBlurFlake), blur2: new BitmapParticleMaterial(medBlurFlake), blur3: new BitmapParticleMaterial(heavyBlurFlake) }; snowHolder.addChild(flakeHolder); flakes.push(flakeHolder); } scene.addChild(snowHolder); singleRender(); addEventListener(Event.ENTER_FRAME, enterFrame); } private function createBitmaps():void { var lightBlur:BlurFilter = new BlurFilter(4, 4, 1); var medBlur:BlurFilter = new BlurFilter (8, 8, 1); var heavyBlur:BlurFilter = new BlurFilter(16, 16, 1); var type:String = GradientType.RADIAL; var colors:Array = [0xFFFFFF, 0xFFFFFF, 0xFFFFFF]; var alphas:Array = [1, .1, 0]; var ratios:Array = [0, 200, 255]; var mat:Matrix = new Matrix(); mat.createGradientBox(10, 10); var baseFlake:Sprite = new Sprite(); baseFlake.graphics.beginGradientFill(type, colors, alphas, ratios, mat); baseFlake.graphics.drawCircle(5, 5, 5); baseFlake.graphics.endFill(); noBlurFlake = new BitmapData(10, 10, true, 0x00000000); noBlurFlake.draw(baseFlake); ltBlurFlake = new BitmapData(10, 10, true, 0x00000000); ltBlurFlake.draw(baseFlake); ltBlurFlake.applyFilter(ltBlurFlake, baseFlake.getBounds(baseFlake), new Point(), lightBlur); medBlurFlake = new BitmapData(10, 10, true, 0x00000000); medBlurFlake.draw(baseFlake); medBlurFlake.applyFilter(medBlurFlake, baseFlake.getBounds(baseFlake), new Point(), medBlur); heavyBlurFlake = new BitmapData(10, 10, true, 0x00000000); heavyBlurFlake.draw(baseFlake); heavyBlurFlake.applyFilter(heavyBlurFlake, baseFlake.getBounds(baseFlake), new Point(), heavyBlur); } function enterFrame(e:Event):void { var pan:Number = cam.rotationY - 210 * stage.mouseX / (stage.stageWidth/2); pan = Math.max( -100, Math.min( pan, 100 ) ); cam.rotationY -= pan / 12; var len:int = flakes.length; for(var i:int = 0; i < len; i++) { var p:Particles = flakes[i]; if (Math.abs(p.sceneZ) <= 100) { p.particles[0].material = p.extra["noblur"] } if (Math.abs(p.sceneZ) >= 101 && Math.abs(p.z) <= 200) { p.particles[0].material = p.extra["blur1"] } if (Math.abs(p.sceneZ) >= 201 && Math.abs(p.z) <= 300) { p.particles[0].material = p.extra["blur2"] } if (Math.abs(p.sceneZ) >= 301) { p.particles[0].material = p.extra["blur3"] } p.extra["radians"] += (p.extra["K"] / 180) * Math.PI; p.x -= 3 * (Math.cos(p.extra["radians"])); p.z -= 3 * (Math.sin(p.extra["radians"])); p.y -= p.extra["I"]; if (p.y < -2000) { p.y = 2000; p.x = randRange(-2000, 2000); p.z = randRange( -2000, 2000); p.extra["I"] = randRange(2, 8); } } singleRender(); } private function randRange(min:Number, max:Number):Number { return Math.floor(Math.random() * (max - min + 1)) + min; } } } |
And an example (with panorama):
Sorry, the comment form is closed at this time.