Rss

Archives for : October2013

Building the Milky Way

Continuing on my multi-part blog about rendering space, now I’ll talk about building the Milky Way.

Rendering the galaxy was much simpler than rendering the stars. Basically, put a texture of the Milky Way on the inside of a sphere, and you’re done!

Okay, not a sphere, but a rough approximation of a sphere. Start with a cube, and since the GPU will only draw triangles, construct each square facet from two triangles. Subdivide each triangle into two more triangles, and then subdivide all those triangles into two more triangles. Now turn it into a pseudo-sphere by normalizing all the vectors to be a distance of 1 from the center. Presto–a 48-sided polyhedron, or “sphere”.

The Milky Way gets plastered on the inside surface of the sphere, since we’re viewing it from the inside. We use a great image from the European Southern Observatory. This gets cut up into a cube map so we can use it within our shader.

Add noise to the render so we break up the pixelation.  See my GLSL random number blog for more details on this and the noise function itself.

And we’re done!  Much simpler than the stars. The earth won’t be so simple.

(Here’s my dirty little secret–I never checked the milkyway texture with the actual stars, so I’m quite certain they are misaligned. It was on my list of things to check for the longest time, but I had so much else to finish that I just dropped it at some point. My bad.)

Building the Stars

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.

Taurus

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.

Pleiades

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   #9db4ff
  B1(V)     162 185 255   #a2b9ff
  B3(V)     167 188 255   #a7bcff
  B5(V)     170 191 255   #aabfff
  B8(V)     175 195 255   #afc3ff
  A1(V)     186 204 255   #baccff
  A3(V)     192 209 255   #c0d1ff
  A5(V)     202 216 255   #cad8ff
  F0(V)     228 232 255   #e4e8ff
  F2(V)     237 238 255   #edeeff
  F5(V)     251 248 255   #fbf8ff
  F8(V)     255 249 249   #fff9f9
  G2(V)     255 245 236   #fff5ec
  G5(V)     255 244 232   #fff4e8
  G8(V)     255 241 223   #fff1df
  K0(V)     255 235 209   #ffebd1
  K4(V)     255 215 174   #ffd7ae
  K7(V)     255 198 144   #ffc690
  M2(V)     255 190 127   #ffbe7f
  M4(V)     255 187 123   #ffbb7b
  M6(V)     255 187 123   #ffbb7b

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.

Why?

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.

Pleiades_large

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.

pleiades-closeup

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?

big-star

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!

 

What “Gravity” gets right (SPOILER ALERT)

If you plan to see Gravity, and you don’t like spoiled endings, STOP READING NOW.

Now that I’ve put in my required disclaimers, we can talk freely. I saw Gravity last night with some friends, and I loved it. What’s not to love about an epic 3D visual extravaganza set in space? Especially one with the lovely Ms Bullock battling orbital debris.

As you might expect, in the process of creating Ablative Air I’ve done quite a bit of research on orbital debris. I’d say I’m as close to an expert on the subject as one might reasonably claim as a layperson. There were a couple of the things that rang my “scientific credibility” alarm bells.

  • Debris wiping out all communication with ground control.  Houston should be able to reach the astronauts for a window at least once every 90 minutes when they’re within visual range with ground antennas. Not to mention that comms sats are usually in geostationary orbit, thousands of kilometers above where the destruction belt was occurring.
  • All the stations are a little too conveniently close to each other. You could stretch your imagination for the proximity of the Chinese station and ISS if they’re in nearly identical orbits since one of stations is actually hypothetical. However, the Hubble is orbiting a couple hundred km higher than the ISS in real life, and it’s pretty darn impossible for a jet pack to traverse that distance.  They certainly aren’t going to be in visual range.
  • Basketball-size dents showing on the inside of the Chinese station when struck by debris? Being hit by a chunk moving at 11,000km/hour is not going to leave a dent, it’s going to vapourize a massive chunk of the station wall and turn our heroine into a tiny cloud of orbiting ashes.

I choose to excuse Alfonso Cuaró for a few of these liberties he’s taken, because it is a movie, and because there is so much that he does get right.  The zero gravity motion is impeccable. Action and reaction for everything, just like it should be. The visuals of the earth, stars, atmosphere, and aurora are beautiful, and very much in line with the research I’ve done for Ablative Air. And most impressive: the absolute silence of space. It’s something that few other directors have ever dared to try. I love when the escape ship undocks from the exploding space station and everything goes instantly silent.  Cinematically wonderful and absolutely truthful.

I can sympathize with Alfonso. There are lots of little liberties that I’ve taken with Ablative Air too. I struggled throughout development to balance scientific accuracy against the fun factor, just like Alfonso must have. Who wants to make a game/movie that’s scientifically accurate but absolutely zero fun to play/watch?

There’s one thing that I removed from the prototype of Ablative Air that Alfonso built his entire movie around. A runaway debris collision chain, otherwise known as “Kessler syndrome“. When two pieces of debris collide, they create even more debris. And when enough debris exists at a particular altitude, there will be so many collisions that more debris is created than can be cleared by a natural orbital decay. In effect, this becomes an untraversable shell of bullets zipping around the planet. This was first proposed by NASA scientist Donald Kessler in 1978, and it has been verified in simulation models.

I originally did emulate the Kessler syndrome within Ablative Air, exactly as Mr. Kessler predicted.  Not really on purpose–it spontaneously emerged out of my debris simulation. Originally when debris trajectories intersected they would collide, conserving the total mass of the two chunks but fragmenting into many smaller independent pieces. I found in playing this early prototype of the game that once you hit a certain threshold, play became impossible. Collisions were happening continually, and by the time you got rid of one chunk of debris, fifty more would be in its place. I tried putting a cap on the total debris count, or arbitrarily ending the game when the debris density got too high.

Neither of these were very good fixes. It was just too frustrating to play with collision-generated debris. Once you got anywhere near the critical debris density, any space station would be obliterated in a spray of debris within a second by a dense fog of rapidly moving projectiles. (Of course, when Gravity got to this “massive obliteration” stage near the end, it made me recall my own mini-simulations and I broke out into a big grin.)

My solution as implemented in the final game, was to test debris only for a collision with the space station, and no longer test for collisions between any two arbitrary pieces of debris. I really struggled with cutting out debris collisions since I knew it wasn’t being technically accurate, but I made the difficult choice to remove collisions because it made a much more enjoyable game. Sometimes you just have to sacrifice reality for fun.

I didn’t get Kessler syndrome right, but Gravity did. So I can forgive the other places where Gravity goes scientifically soft. I have a renewed well of sympathy for making the hard choices.