Physically-Based Rendered Volumetric Atmosphere
Have you ever thought about why the sky is blue? Or perhaps why the sunset is red? It's because the atmosphere isn't really transparent.
All those light rays don't just travel millions of miles away from the sun straight to your eyes, instead they bounce around in the atmosphere before they reach us. The particles in the atmosphere aren't perfect mirrors, so they absorb little bits of light each time a photon reflects off of them. Based on the molecules that make up our atmosphere we can predict and how these bounces and scattering alters the wavelength of the light. This is called Rayleigh scattering, and it's responsible for that awesome picture below.
I don't want to get too deep into the physics; I'm a computer guy, as long as it looks good enough I'm happy. Also because I'm a computer guy, if my sunset is lagging, then I'm definitely not happy. If I want to simulate a spot on the ground at sunset, I have to calculate the color of every light ray that hits each spot on the world from all directions throughout the atmosphere. And I need to do that for every spot on both the ground and sky! That's wayy too much to calculate.
Do you see how just one point needs to calculate the light coming in from all directions? Look at many steps I need follow that purple ray back towards the sun. Remember there is also light travelling towards the point from the far side of the atmosphere.
But thanks to a couple breakthrough papers, there are ways to use some crazy calculus to precalculate a values for the scattering of each point in the sky. Then these values can be stored in textures as lookup tables, which we then use to finish the easy parts of the calculation. You end up with a pretty good looking atmosphere that doesn't lag your computer.
Building the demo
I built this demo with my extremely talented friends Christian and Luke without using any high-level libraries, just pure WebGL. We used it as the final project to our computer graphics class. The three of us all had some graphics experience, but it was pretty cool when to hear our professor say "I know I'm doing a good job when my students become better than me". Our project was voted the best in the class.
To draw the scene, we created a set of large mathematical formulas that calculate the exact color of each pixel in each frame, using only a pixel’s coordinates and timestamp as inputs.
What makes this especially hard for the atmosphere is that countless light rays reflect from all over the atmosphere to each spot on Earth, so every pixel would require thousands of calculations. Since we render over 200 million pixels per second, that might add up to trillions of operations per second. Now for the hardest part, even though the run these calculations on your GPU, it can’t handle this many. We need to find some clever approximations that don’t compromise the visual effect.
The primary approximation for the atmosphere uses some pretty dense calculus. This math solves as much of the total atmospheric interaction as possible ahead of time. Then, by storing the results in a table, we can offload significant parts of this formula to a single lookup, reducing the number of calculations from thousands per pixel to mere tens. This comes at the cost of only slight banding at certain extremes like sundown where the table loses precision. I didn’t invent the ingenious math behind this, so a link to the original paper is below.
The procedural clouds use a similar technique but with different optimizations since the calculus doesn't work with noise.
After a couple weeks of scaffolding and heavy reading, we implemented most of this demo in one epic all nighter in the lobby of a school building. It was one of my favorite coding memories. I'll never forget after working on the atmosphere all night, being taunted by a particularly beautiful real sunrise. Nature can be so cruel.
My only regret was that we picked a free 3D file for the birdbath, and some of the normal vectors aren't set up right. Whatever, it was free.