This is the first part of a multi-part blog about how I created various visual features of Ablative Air.
Much of the visual appeal to the game comes from the realistically rendered space and earth graphics. While those features aren’t essential to game play, they do make the game fun to look at, as well as make for an interesting story (at least so my friends tell me). I’ll start with the stars, because it was the first thing I started with on the game.
There are about 6000 or so stars visible with the naked eye. There are really only three qualities of a star we can assess with our eyes: position (referred to by astronomers through right ascension and declination), brightness (referred to by astronomers as apparent magnitude) and color (referred to by astronomers as color). Let’s start here: The Bright Star Catalogue, 5th Revised Ed., Hoffleit D., Warren Jr W.H. Astronomical Data Center, National Space Science Data Center 1991. This is an astronomical dump about the most common facts known about each visible star and a few more that aren’t visible with the naked eye but are still relatively bright. The catalogue lists about 9000 stars, so I’ll use all of them for the game. (Stands to reason that you would be able to see more stars in space without that pesky atmosphere in the way.)
A star’s position is easy to determine from the catalogue: convert from Right Ascension and Declination to spherical coordinates, then into cartesian coordinates for the game. I may have lost you there on the details, but suffice it to say that it’s straightforward math, and hence quite boring, so I won’t spend any time talking about it.
Much more interesting is the star’s color.
You’d be excused for thinking all the stars in the game are white. They’re not. The effect is subtle, but the game stars are represented with the same colors as we would see stars on earth. Here are a couple examples.
Taurus has a red “eye”, the star Aldebaran, the bright star nearest the upper left corner. When people say it’s a red star, they’re mostly being poetic. I’d agree that it’s not really that red.
Orion in the opening image is another constellation with a recognizably colored star. You’ll see that Betelgeuse is red(ish), just like it’s supposed to be.
Pleiades is pretty easy to spot, and a kind of bluish color.
Star color basically comes from the temperature of the star. There’s a lot of complicated stuff to do with black body radiation and light wavelength, but Mitchell Charity from MIT has done all the hard work already, and I reproduce here his table I’ve started from.
|O5(V)|| 157 180 255
|B1(V)|| 162 185 255
|B3(V)|| 167 188 255
|B5(V)|| 170 191 255
|B8(V)|| 175 195 255
|A1(V)|| 186 204 255
|A3(V)|| 192 209 255
|A5(V)|| 202 216 255
|F0(V)|| 228 232 255
|F2(V)|| 237 238 255
|F5(V)|| 251 248 255
|F8(V)|| 255 249 249
|G2(V)|| 255 245 236
|G5(V)|| 255 244 232
|G8(V)|| 255 241 223
|K0(V)|| 255 235 209
|K4(V)|| 255 215 174
|K7(V)|| 255 198 144
|M2(V)|| 255 190 127
|M4(V)|| 255 187 123
|M6(V)|| 255 187 123
I’ve done a little expanding and interpolating on Mitchell’s list to map stellar classifications onto the basic star colors like so:
# Colors are little-endian words: 0xbbggrr (or Blue Green Red), opposite of normal RGB order
'O5': 0xffb09b, 'O6': 0xffb8a2, 'O7': 0xffb19d, 'O8': 0xffb19d, 'O9': 0xffb29a, 'B0': 0xffb29c, 'B1': 0xffb6a0, 'B2': 0xffb4a0, 'B3': 0xffb9a5, 'B4': 0xffb8a4, 'B5': 0xffbfaa, 'B6': 0xffbdac, 'B7': 0xffbfad, 'B8': 0xffc3b1, 'B9': 0xffc6b5, 'A0': 0xffc9b9, 'A1': 0xffc7b5, 'A2': 0xffcbbb, 'A3': 0xffcfbf, 'A4': 0xffd2cf, 'A5': 0xffd7ca, 'A6': 0xffd4c7, 'A7': 0xffd5c8, 'A8': 0xffded5, 'A9': 0xffe0db, 'F0': 0xffe5e0, 'F1': 0xffeae6, 'F2': 0xffefec, 'F3': 0xffe8e6, 'F4': 0xffe2e0, 'F5': 0xfff7f8, 'F6': 0xfff1f4, 'F7': 0xfff3f6, 'F8': 0xfcf7ff, 'F9': 0xfcf7ff, 'G0': 0xfcf8ff, 'G1': 0xf8f7ff, 'G2': 0xf2f5ff, 'G3': 0xebf3ff, 'G4': 0xe5f1ff, 'G5': 0xeaf4ff, 'G6': 0xebf4ff, 'G7': 0xebf4ff, 'G8': 0xdeedff, 'G9': 0xddefff, 'K0': 0xddeeff, 'K1': 0xbce0ff, 'K2': 0xc4e3ff, 'K3': 0xc3deff, 'K4': 0xb5d8ff, 'K5': 0xa1d2ff, 'K6': 0x95ccff, 'K7': 0x8ec7ff, 'K8': 0xaed1ff, 'M0': 0x8bc3ff, 'M1': 0x8eccff, 'M2': 0x83c4ff, 'M3': 0x81ceff, 'M4': 0x7fc9ff, 'M5': 0x6fccff, 'M6': 0x78c8ff, 'M7': 0x80c4ff, 'M8': 0x6dc6ff,
The letter code is the stellar classification–roughly how big and old the star is.
If you look at Mitchell’s colors based on pure mathematics, they are far more garish than we actually see. There are no carnival reds or blues in my night sky! If stars were big enough to show a disk, they might be the colors that Mitchell has computed, but they are visually so tiny that they’re a point. We need to account for the mechanics of the eye.
The human eye has cells called rods which are responsible for black and white vision, and cones which are responsible for color vision. However, rods are also much higher density, which is why at night when there’s low light we see mostly in shades of gray. We perceive low-level ambient light as being slightly bluish.
Dim objects like stars will also tend to have little color because we mostly perceive them with rods and not cones, and so will look mostly white, maybe with just a hint of blue. To mimic this effect, I alter the color of the star based on the magnitude. The very brightest stars will end up with a good proportion of the “true” star color, and as the magnitude decreases, I wash out a greater proportion of the red and green component of the color. The more dim the star, the more white it will appear. This mimics how the eye perceives the color of the pinpricks of stars, which is why it’s hard to find colored stars in the game –just like in the night sky.
That takes care of color.
Next to solve is showing a star’s brightness. This would seem rather simple–bright stars are a bright pixel, dim stars are a dim pixel. After all, a star is so tiny it’s only a single pixel as far as the display, right? I tried that approach first, and it looked awful.
We don’t see a steady point when on earth. We see the twinkling of a star as it’s bent through the atmosphere. While a twinkle shader would be a fun shader to write, my game shows the stars from outer space without any atmosphere, so the stars must be steady. But us earthbound humans are used to seeing that twinkle. We’re also used to seeing images of stars through a telescope. Both of those things influence what we expect. We expect spikes and a halo around bright stars. Stars don’t really have them, but they looks much more like what we expect when they’re there. Take a look at this picture of the Pleiades.
Although the dim stars just look like points, look at the bright stars of the cluster. If you look carefully, you’ll see there are two levels of halo around the bright stars, and diffraction spikes on the four cardinal points (at 0, 90, 180, and 270 degrees). Those are artifacts of telescope construction, but we’re so used to them and they look so natural that I emulate them. Again for comparison, my rendered Pleiades (without the nebula gas), up close.
Remember that these stars are actually quite small and dim, so they won’t appear as blue as they do in the telescope. Dim stars are points, bright stars show diffraction spikes and halo effects.
What does the halo/spike effect look like up close?
Position: CHECK. Color: CHECK. Magnitude: CHECK.
Hey–wait a minute. The spikes are actually two pixels tall and wide. Why so big?
There’s just one last thing to address, and that’s temporal aliasing. Before I did my star shader, I didn’t know what that was. I found out rather quickly that it’s a necessary problem to address.
Temporal aliasing is an artifact caused by the star in motion across a display with discrete pixel locations. Sweep the view across the sky when you’re using single-pixel stars, and it will look like the stars are “jumping” around. This happens because the relative distance between computed star locations changes, depending on the exact angle. So if you slide the viewport over a tiny bit, the floating point calculation round-off might make two nearby stars appear to jiggle with respect to each other. This effect is difficult to show in a movie, but trust me–it’s very distracting, and it completely destroys the illusion of reality, not to mention smoothness.
So the last tweak to the star shader was to compute a sub-pixel location for the star, and draw the star features (including the spikes) corresponding to the subpixel. That means that if the star location is actually at the center of the pixel we’ll get single pixel spikes. But if it’s exactly between two pixels, we get a blurred two-pixel wide spike. Although this doesn’t look perfect in a highly magnified view like above, it looks absolutely beautiful when the stars are in motion like they are in the game.
Star shader: DONE!