The most difficult shader in Ablative Air is the Earth, but it’s also the most fun to discuss.
Earth’s surface.
Like the Milky Way, the earth is represented by a sphere, or as close as we can get to one by creating a finely faceted polyhedron. But where the Milky Way is a single texture applied to the inside of a sphere, the Earth is several textures blended together applied to the outside of a sphere. The first layer is the Earth’s surface. There are a number of people who have created public domain earth images through NASA satellite imagery, so we leverage the effort of those fine folks.
What does that look like wrapped around the globe?
Pretty sterile–we’re missing clouds. This is relatively straightforward–we take a second texture, and add it on top. Why not just add the clouds to the original image? Because I want them to move independently of the surface. I just slide them across the surface so they’re not “accurate”, but the moving clouds definitely adds to the appeal, even if they’re not meteorologically sound. (I did consider creating a cloud-based animation that would show real weather patterns, but I decided against it–too much effort and time and very few would likely notice.)
Okay, now we have to add in the night side. It’s one thing to make it dark, but we also need to add in night-side city lights. Thankfully the fine folks at NASA have created images of the night side of the earth minus clouds that show the city lights nicely. To save on texture RAM, I don’t use a color image, but just intensity so I know how bright the lights are at any one point on the earth. However, to make it more realistic than just using white, I use a slightly yellowish hue–the same color as the spectrum of sodium lights, which form the majority of light pollution.
One final touch on the lighting. In closeups on the rotating earth, it looked too artificial for the lights to suddenly pop on and off as soon as night came. I look for the day/night transition, and to emulate twilight, I fade the lights gently in and out near the day/night boundary to emulate the good people of earth waking up or starting their nightlife.
Are we done? Nope. The earth has a good proportion of water, so we want specular highlights (bright areas) where the sun reflects off the ocean or lakes. We need another map of the earth’s water, and we use this as a boolean gloss map: if there is a white pixel, we apply a specular highlight because it’s a watery surface, and if it’s black, we don’t. We compute the reflection angle of the sun, and spread out the highlight as that angle deviates from the user’s viewing angle. Sounds hard, but it’s not too difficult.
The specular highlighting looks especially good when you can see the sunlight reflecting off the ocean, stopping when it crosses land, but then picking up bright reflections again when the sun crosses over large inland lakes or seas (like the Great Lakes, or Caspian Sea).
Again, to save on texture memory, I combine the watery gloss map, night side city lights, and cloud texture into a single texture, using the red (for clouds), green (for night lights), and blue (for water map) channels. I load the single combined texture, then pull the individual values apart in the shader when I need to apply the specific coloring or highlights. (Just for fun, here’s what the resulting texture looks like–kinda like a Earth-style Picasso.)
All the techniques applied for a complex but beautiful earth shader…
Wait a second! Something’s wrong still… The atmosphere is extremely thin compared to the planet, but it does have an effect, especially when the sun is behind the planet. So I added one more final touch: I put a thin shell around the planet, and tuned the color depending on the angle of the sun to the planet’s limb (edge). Doing a real atmospheric refraction calculation is very complex due to the different compositions and temperatures of the layers of the atmosphere, so I completely cheat here. I put into the shader something that reacts to the angle of the sun, fades from white to blue moving from the planet surface towards outer space, added transparency, and just played with the fine tuning of the values until I was happy with the look of the result.
Instead of building another spherical shell around the planet with a new shader, I implemented another massive cheat. In the earth shader I calculate the surface normal: when that normal is just shy of perpendicular to the viewer (that is, very close to the outer ring defining the edge of the sphere), I switch from calculating a surface pixel to calculating an atmospheric pixel. The net effect is that I don’t need to compute a whole new sphere when I want only the very edge of the sphere to be affected anyway. The atmospheric ring does truncate a small number of earth surface pixels from being displayed, since those pixels are replaced by the atmosphere refraction effects. This very slightly distorts the planet edge since the rotating surface land comes into view slightly more abruptly than it normally would, but it isn’t really noticeable even when you’re looking for the effect.
I’ve finally come to groc shaders: it’s not how you do it, it’s how it looks that’s important. You don’t need to mimic reality, you just need to fool the eye.
Here’s a snapshot of the game in progress to show the final result.