Next up is rendering the surface of the earth. Luckily the Nasa Earth Observatory provides an excellent high resolution data set for free. The Blue Marble has painstakingly been stitched together from thousands of individual satellite images and corrected for distortions, seams, color inconsistencies and cloud cover. Applying this texture to a sphere already looks quite good. It would be nice to see mountains though, so I learn how to write GLSL shaders in order to apply bump mapping. Shaders are small programs that run on the graphics hardware. Vertex shaders transform the geometry while fragment shaders work with pixel data. This is my first contact with the technology and I'm impressed how easy and expressive the language is. It's based on C syntax which I'm very familiar with, but adds powerful primitives for performing linear algebra and parallel processing of texture maps. Bump mapping takes a gray scale texture map as input and interprets the pixel values as elevation data, computing light reflections from that. I'm immediately dissatisfied with the results as the transition from the day side of the planet to the night side causes stark discontinuities. So I ditch bump mapping in favor of normal mapping. It's the same idea but instead of storing elevation values in a gray scale texture map you use a three channel color map to store the three components of the surface normal. In essence you are storing the first derivative or slope instead of the absolute values. The results are much smoother.
I'm using Phong Shading, a relatively accurate light reflection model. However it can cause overly bright highlights which make the material seem like smooth plastic. To avoid this effect I employ specular texture maps. Generally these indicate the reflectivity of every pixel. For the earth a simple binary map is enough - bodies of water are highly reflective while landmasses are not. You can see a highlight on the ocean, lakes and rivers but not on land.
Next up is the dark side of the planet. When the sun goes down electric lights go on. I use a texture map of the major metropolitan areas on the planet and gradually blend this in on the dark side.
Lastly I overlay a layer of clouds that slowly circles the globe. Since on an average day about 70% of the planet are covered in clouds I have chosen an artificially clear day. This is supposed to be a game eventually after all and realism shouldn't hurt playability. Having the entire planet obscured by clouds is no fun.
To add one final touch to the appearance of the earth I add another shader pass to render an atmosphere. Atmospheric scattering is primarily caused by two effects: Rayleigh Scattering and Mie Scattering. Rayleigh Scattering is caused by small molecules in the air and scatters light of shorter wavelengths much more than longer ones. This is the reason the sky seems blue to an observer - the short wavelength blue light is scattered all over the place and reaches you from all sides. Mie Scattering is caused by larger dust or water particles and is responsible for rainbows or a halo around the sun. Sean O'Neil has published an excellent article in GPU Gems implementing a stochastic approximation of these effects on graphics hardware. It shoots a bunch of sampling rays through the atmosphere and derives a color value from that. The effect is quite dramatic and beautiful.
Of course the earth isn't alone in space so I use Google image search to find a nice photograph of the Milky Way and tape that on the inside of a sky sphere, producing the starry background you see in the screen shots. Everything until this point is rendered with a perspective camera, causing all lines to converge onto a distant vanishing point. For all intents and purposes the sun is infinitely far away and shouldn't scale based on the observer's position. If I put the sun into the 3D scene it would either have to be much too close or cause numerical instability because of the hugely different scales involved in rendering pixels on the earth's surface vs the sun. So instead I add another scene on top of the 3D scene. This one uses an orthographic projection, thus having no perspective distortion. I render a two dimensional sprite for the sun onto this one. I've also added some lens flares for flair. Lens flares are reflections within the lens of a camera if you point it more or less directly at a light source. For these to work I need to do some fancy math to figure out whether the light source is obscured or not. Generic implementations of lens flares typically resort to using a stochastic process based on occlusion maps calculated on the graphics hardware. Since my scene is rather simple I can get by with calculating the exact ray casting solution.
Finally I render yet another layer on top of the existing 3D and 2D cameras. This last layer is a regular HTML/CSS based website. I use jQuery UI on this to create a user interface with buttons and text as one is used to.
Of course all of this work is rather useless if I can't serve it to players (hopefully that'll include you! ;-)). I decided to use Google App Engine as my host. Google's cloud platform offers a ton of APIs, a free hosting tier and infinite scalability. I dislike Python and Java, which leaves Go as an App Engine supported language choice on the server side. My experience with it is minimal, but so far I'm impressed with its simplicity and elegance. People much more knowledgeable than me keep raving about its virtues, so it must be on to something.
App Engine uses Bigtable as its storage backend. This trades off the convenience a regular SQL database offers with its ACID guarantees for near infinite scalability, a weak query language and eventual consistency. Makes programming against it a little tricky since you are operating at a much lower level and have to be mindful of your transactions.
Anyway. Of course you don't have to take my word for any of this but can try the earth yourself. It requires decent hardware and a good browser (sorry, Internet Explorer doesn't count). That said, I've run it on a phone, a tablet and various laptops and PCs.
Here you go: Operation Survival - Globe Rendering Test.