How to generate a starry night sky in Unity using real-world data.

⚠️ This implementation does not work anymore with the latest version of Unity.

The data-gathering part of this article is still relevant, but the particle system implementation is defunct.

Creating a star field in Unity is not that tough, and in this article I will describe how to create a star field with a particle system using real-world data of the stars we see in the night sky. And all in only one drawcall!

DATA, DATA, DATA.

For this, we will use the HYG database, a combined database collected by David Nash, which contains a whole bunch of data on about 100.000 stars, but there’s 4 variables we’re interested in:

the apparent magnitude (brightness) of the star, and it’s Cartesian (XYZ) coordinate is space, relative to earth.

With this data, it becomes incredibly easy to put the stars into our own night sky.

We open the .csv sheet (Comma Separated Value, a basic spreadsheet) in a software like Microsoft Excel, or Libreoffice Calc. The procedure will be the same in both programs.

Isolate the data we’re insterested (titled “mag”, “x”, “y”, and “z”) and then remove the titles.

Just copy and past the columns so that they are in the following order: (mag, x, y, z).

After we isolated the data, we will sort the rows based on the magnitude, the first column.

The procedure is the same in Excel and Libreoffice Calc, select the four columns, and go to data > sort. Sort by the column A, in ascending order.

Don’t be fooled, the apparent magnitude of a star is inverse to it’s brightness! Stranger still, some bright stars go below zero!

 

The top value should be somewhere around -26.7, with very small coordinates; that is our sun! We can remove this one, as it’s position is dependant on the time of year.

The next one is at -1.44, which is Sirius.

Now that our data is in order, we can move on to Unity, and have some fun.

SPAWNING THE PARTICLES.

In Unity, create a new particle system, disable everything except “emission” and “renderer”.

Set the rate of the emission to 0, the scale of the particles to around 10, and the speed to 0 (important).

Create a new C# script, I called mine “StarParticleCreator”

We need an Awake() and a Start(), but no Update().

We also need a public integer that will serve as our maximum particle count, as we probably don’t want to draw ALL 100.000 stars.

(but we will, don’t worry ? )

Attach the script to the particle system, and we’re ready to begin.

In the Awake() function, we will set the burst array of the particle system. We do this to spawn that amount of particles at the beginning of the scene.

public ParticleSystem particleSystem;
public int maxParticles = 100;
public TextAsset starCSV;
void Awake () {
   particleSystem = GetComponent<ParticleSystem>();
   particleSystem.maxParticles = maxParticles;
    ParticleSystem.Burst[] bursts = new ParticleSystem.Burst[1];
   bursts[0].minCount = (short)maxParticles;
   bursts[0].maxCount = (short)maxParticles;
   bursts[0].time = 0.0f;
    particleSystem.emission.SetBursts(bursts, 1);
}

The bursts must be given as an array of bursts, but we will just create and array with a size of 1.

READING THE FILE

Now comes the fun part. We’re going to read the file, and put the particles in the right place during the Start().

We create a new public TextAsset called starCSV, and in the Unity inspector, we draw the .csv sheet we processed earlier.

We create a new array of strings, called string[] lines. We get the text from the TextAsset, which will be one giant string, and break it up for every new line, with simply the folowing line:

string[] lines = starCSV.text.Split(‘\n’);

We create ParticleSystem.Particle array, which will hold all the particles in the system.
ParticleSystem.Particle[] particleStars = new ParticleSystem.Particle[maxParticles];particleSystem.GetParticles(particleStars);

Now we can loop over the lines, and do something for every particle.

Withing the forloop, we will create a new array of strings, which will hold the components of each line, separated by comma.
string[] components = lines[i].Split(‘,’);

Element 0 will contain the brightness, and element 1-3 will contain the star’s position.

The code to put the particle at the stars position is not the prettiest, but here’s how it works.

The Cartesian coordinates we were given were not in the same coordinate system Unity uses.

It is described on the website as the following,

“X,Y,Z: The Cartesian coordinates of the star, in a system based on the equatorial coordinates as seen from Earth. +X is in the direction of the vernal equinox (at epoch 2000), +Z towards the north celestial pole, and +Y in the direction of R.A. 6 hours, declination 0 degrees.”

It sounds very complicated, but it comes down to this:

Xunity = Xstar, Yunity = Zstar, Zunity = Ystar.

We can use float.Parse() to change the components into floats.
new Vector3(float.Parse(components[1]), float.Parse(components[3]),  float.Parse(components[2]))

Note that component 2 and 3 are flipped.

This creates a new Vector3 with the right positions for the stars.

We also want to set the lifetime of each particle to Infinity.

After we exit the forloop, we will put the particles back into the particle system.

void Start()
{
    string[] lines = starCSV.text.Split('\n');
    ParticleSystem.Particle[] particleStars = new ParticleSystem.Particle[maxParticles];
    particleSystem.GetParticles(particleStars);
    for (int i = 0; i < maxParticles; i++)
    {
        string[] components = lines[i].Split(',');
        particleStars[i].position = new Vector3(float.Parse(components[1]),
                                                float.Parse(components[3]),
                                                float.Parse(components[2]));
        particleStars[i].lifetime = Mathf.Infinity;
    }
    particleSystem.SetParticles(particleStars, maxParticles);
}

Let’s set the particle count in the Unity inspector to 100.000, and check out the results.

Wow! Spectacular. As you can see, there’s a whole bunch of stars around our planet, and these are just the 100.000 that appear brightest to us out of the ~400.000.000.000 in our milky way alone.

However, because we are dealing with stars on a stationary night sky, we can normalize these vectors, and multiply them with a large number, to put them in the right position on our screen. We will multiply the normalized value with the far clipping plane of the main camera, minus a little bit.

This is a lot easier to work with, but still incredibly chaotic. If we tone down the maximum stars to around 1000, you can kind of make out Orion and the Pleiades.

 

But it doesn’t look quite nice yet. We should take the brightness of the star into account as well.

The equation to do so is a bit unorthodox, somewhere between an art and a science.

I found that

Color.white * (1.0f – ((float.Parse(components[0]) + 1.44f) / 8));

produces nice looking results.

(exaggerated for easy viewing)

To describe what’s happening:

We parse the brightness value of each star, and add 1.44. We do this because the brightest star is at -1.44. We divide it by 8, and then inverse it (1-x).

When rendering a natural amount of stars (about 1000), dividing by 8 produces nice results. For more stars, go for 10 or 15.

Or try for yourself and see what works for you, maybe raising it to a power, or sampling from a gradient ?

CHECKING

There’s this website that analyzes pictures of stars you throw at it. To check if your script is functioning correctly, upload one and see what happens!

(http://nova.astrometry.net/)

It detects ours just fine! Hooray!

IN Practice

In practice, there’s a few things you might want to consider.

First of all, you’d probably want to cap off the .CSV file, as right now it is a whopping 4mb.

Also, to render more stylized looking stars, you could opt for rendering small billboards of streaks, but beware of overdraw!

Limit the amount of stars you draw depending on your target platform. Although handling particles is Unity’s business, and it’s only one drawcall, it’s still worth profiling!

How to generate a starry night sky in Unity using real-world data.

One thought on “How to generate a starry night sky in Unity using real-world data.

Leave a Reply

Your email address will not be published. Required fields are marked *