Erain 3D
-->

Author: makc
Date: May 2nd 2008
version: 3.0.3

Starry sky

Objective of the tutorial

In this tutorial we will see how to employ new StarField class to create a sky full of stars.

Setting things up

We believe that you have studied entry level 3.0 tutorials, so to save some time we will start from this code stub:

package {
	import flash.display.*;
	import flash.events.*
	import flash.filters.*
	import sandy.core.*;
	import sandy.core.data.*;
	import sandy.core.scenegraph.*;
	import sandy.primitive.*;
	import sandy.materials.*;

	public class Sky extends Sprite {
		// screen size
		private var w = 400;
		private var h = 300;
		// sandy scene
		private var scene:Scene3D;
		// sky starfield
		private var sky:StarField;


		public function Sky () {
			// set up scene
			scene = new Scene3D ("", this, new Camera3D (w, h), new Group (""))
			// set up sky
			sky = new StarField ();
			// TODO make stars :)
			scene.root.addChild (sky);
			// subscribe to Event.ENTER_FRAME
			addEventListener (Event.ENTER_FRAME, enterFrameHandler);
		}


		private function enterFrameHandler (event:Event):void {
			// render the scene
			scene.render ();
		}
	}
}
This is document class for empty 400×300 FLA that creates empty StarField object, adds it to the scene and then renders it every frame. Boring.

Making stars

How does one uniformly distribute points on a sphere? In ties demo I used “brute force” solution; since then some kind people have posted exact solutions on PV3D mailing list. Here we will be using 1st one of those.

Ok, so let's write the code under “TODO” comment. StarField class objects have stars property, which is array of Vertex class objects. In order to create new star, we simply have to make new Vertex object and add it to array:

// TODO make stars :)
var N:int = 10000, radius:Number = 100;
for (var i:int = 0; i < N; i++) {
	var phi:Number = Math.acos (-1 + (2*i -1) / N);
	var theta:Number = Math.sqrt (N * Math.PI) * phi;
	sky.stars [i] = new Vertex (
		/* x */ radius * Math.cos (theta) * Math.sin (phi),
		/* y */ radius * Math.sin (theta) * Math.sin (phi),
		/* z */ radius * Math.cos (phi)
	);
}

I have deliberately set radius value to as low as 100 so that we could see all the stars in a starfield. If you would test it like that, you could see that it generates a sphere of equally spaced stars. Real sky, however, has its stars in irregular random pattern. So, we have to add a bit of chaos to this otherwise perfect order, just before making i-th star:

phi   += 0.1 * (Math.random () - 0.5);
theta += 0.1 * (Math.random () - 0.5);

Final touches

Now we can set our sky radius to something bigger to get the camera inside, for example, 500. Let's make our stars shine by attaching GlowFilter to starfield container:

sky.container.filters = [ new GlowFilter (0x7FFF, 1, 6, 6, 10) ];

Finally, let's add some motion in enterFrameHandler:

sky.rotateY += 0.1;

The result

If you actually followed the tutorial, you should have ended up with the code like this:

package {
	import flash.display.*;
	import flash.events.*
	import flash.filters.*
	import sandy.core.*;
	import sandy.core.data.*;
	import sandy.core.scenegraph.*;
	import sandy.primitive.*;
	import sandy.materials.*;

	public class Sky extends Sprite {
		// screen size
		private var w = 400;
		private var h = 300;
		// sandy scene
		private var scene:Scene3D;
		// sky starfield
		private var sky:StarField;


		public function Sky () {
			// set up scene
			scene = new Scene3D ("", this, new Camera3D (w, h), new Group (""))
			// set up sky
			sky = new StarField ();
			// make stars
			var N:int = 10000, radius:Number = 500;
			for (var i:int = 0; i < N; i++) {
				var phi:Number = Math.acos (-1 + (2*i -1) / N);
				var theta:Number = Math.sqrt (N * Math.PI) * phi;
				phi   += 0.1 * (Math.random () - 0.5);
				theta += 0.1 * (Math.random () - 0.5);
				sky.stars [i] = new Vertex (
					/* x */ radius * Math.cos(theta) * Math.sin(phi),
					/* y */ radius * Math.sin(theta) * Math.sin(phi),
					/* z */ radius * Math.cos(phi)
				);
			}
			sky.container.filters = [ new GlowFilter (0x7FFF, 1, 6, 6, 10) ];
			scene.root.addChild (sky);
			// subscribe to Event.ENTER_FRAME
			addEventListener (Event.ENTER_FRAME, enterFrameHandler);
		}


		private function enterFrameHandler (event:Event):void {
			// render the scene
			sky.rotateY += 0.1;
			scene.render ();
		}
	}
}