Starfield Effect

Demo: Starfield Effect

In my introductory post on Real-time rendering I briefly mentioned that one of the earliest demos was the Starfield effect. This is because the Starfield is generally considered the simplest interesting effect in computer graphics. All you need to accomplish this effect is an efficient plot pixel routine and some basic math.

This post is going to show in detail how to create this simple effect using my Rendering Framework project as the foundation.

Effect Overview

The reason a Starfield is generally considered the simplest demo effect is because the entire effect is simply a list stars, which essentially consist of a 3D position (x, y and z coordinate) and a color (white intensity for this example). The stars are initialized with a random value spread out across a boundary defined by some constants in our code (SpreadX, SpreadY and SpreadZ in the sample below) and use a constant speed to update the star’s position each frame. Once a star has surpassed its life span it will be initialized inside the initial predefined boundary, and this cycle is repeated until the effect is over.

Initialize the Engine

Here is the entry point for the application. This simply creates a new instance of a RenderingMemory engine, which is a concrete implementation of a RenderingCore that utilizes GDI+ and exposes an IRenderSurface interface, for efficient per-pixel operations not usually available through the GDI+ Graphics class, to the rendering call-back routine.

using ( RenderingMemory engine = new RenderingMemory( ApplicationName ) )
{
    engine.SetFrameLimit( 60 );
    engine.SetRender( Render );
    engine.SetColorFormat( ColorFormat.Monochrome );

    engine.ClearEveryFrame = true;
    engine.BackgroundColor = Color.Black;

    engine.Start();
}

The interesting things to note about the sample above is that we’re using the Monochrome color format, which is an 8 bit-per-pixel color format that uses the 256 available colors to represent the intensity color – commonly known as Grayscale. The other important thing to notice is that we’re passing a call-back function to the engine, this is the real “meat” of the application. Whenever the rendering form is in an idle state it can render a new frame, this call-back is invoked to draw on the back buffer and the framework will update the contents of the client area with the newly drawn scene.

Starfield Initialization

To set the initial state of the Starfield we need to create a list that contains the Stars we’re going to render. For this effect I chose 1500 stars, which is a constant value refereed to as MaxStarCount. Since the instance constructor for a Star contains all the initialization logic, all we need to do is initialize the List<Star> and add the appropriate amount of Stars to it.

static void InitializeStarfield()
{
    starfield = new List( MaxStarCount );

    for ( int index = 0; index < MaxStarCount; index++ )
    {
        starfield.Add
        (
            new Star()
        );
    }
}

The Star Object

The Star class encapsulates all the logic for this effect.

The Star class has four public read-only fields – X, Y, Z and Color. The object itself has a static constructor that creates a new shared instance of a Random object used for the internal initialization routine as well as an instance constructor that initializes the previously mentioned instance field values. There is a single publicly available method on the Star class, Move, which will update the stars Z position, or reset its values if it’s no longer visible.

Let’s take a look at the Star class.

class Star
{
    #region Definitions

    const Single Speed = 0.125f;

    const int SpreadX = 1500;
    const int SpreadY = 1500;
    const int SpreadZ = 15;

    #endregion

    #region Fields

    static Random randomizer;

    #endregion

    #region Constructors

    static Star()
    {
        randomizer =
            new Random( DateTime.Now.Millisecond );
    }

    public Star()
    {
        Initialize();
    }

    #endregion

    #region Fields

    double x, y, z;

    public int X { get { return ( int ) x; } }
    public int Y { get { return ( int ) y; } }
    public int Z { get { return ( int ) Math.Ceiling( z ); } }

    public Color Color
    {
        get
        {
            int starColor =
                ( int ) Math.Ceiling( ( 1 - ( z / SpreadZ ) ) * 255 );

            return Color.FromArgb( starColor, starColor, starColor );
        }
    }

    #endregion

    #region Public Interface

    public void Move()
    {
        z -= Speed;

        if( z         {
            Initialize();
        }
    }

    #endregion

    #region Internal

    void Initialize()
    {
        x = ( randomizer.NextDouble() - .5f ) * SpreadX;
        y = ( randomizer.NextDouble() - .5f ) * SpreadY;
        z = ( randomizer.NextDouble() + .000000001f ) * SpreadZ;
    }

    #endregion
}

This code is pretty self-explanatory.

The Render Routine

All that’s left is the main render call-back routine. Since all the logic in the application is sealed inside the Star object, we simply iterate over the Star collection and plot a pixel at each Stars coordinates converting its 3d position to a 2d position on our surface, which is the only real trick to this effect. To translate a stars 3D coordinate to a 2D one that maps directly to our surface we simply divide its X and Y values by Z, then translate the star so it’s offset into the middle of our surface. This will give the “3D” effect of the moving Starfield.

static void Render( IRenderSurface display )
{
    int middleHeight = display.Height / 2;
    int middleWidth = display.Width / 2;

    foreach( Star star in starfield )
    {
        display.PlotPixelSafe
        (
            new Point()
            {
                X = ( int ) ( ( star.X / star.Z ) + middleWidth ),
                Y = ( int ) ( ( star.Y / star.Z ) + middleHeight )
            },
            star.Color
        );

        star.Move();
    }
}

And that’s it.

That’s all there is to the Starfield effect. The full project source code for this effect is included in my Rendering Framework project.

Advertisements
By Brandon Tagged

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s