Saturday, November 17, 2018

Updated Planets

I'm taking a break from my procedural city to work on a variety of other smaller projects. I found an interesting planet generator created by colordoge and posted on Reddit and GitHub. I liked the look of these planets, so I asked the author what noise functions he used and tried to do something similar in 3DWorld. He later shared his source code, but by that time I had already figured out what he did. This led to some updates to the universe mode planet shader used to draw most of the rocky planet types. [Actually, all planets use a single shader, so I really mean the rocky planet control flow path.]

I decided to include domain warp noise to produce a more interesting planetary landscape. This was the major difference between our planet generators. I added domain warping to my 2D ground mode and tiled terrain heightmaps last year, so it makes sense to extend that system to 3D for planets. It was straightforward to extend the 2D noise function to 3D. I definitely think these noise function changes improve the variety and realism of planet terrains.

However, these changes do come with a cost of increased GPU draw time, which lowers frame rates. I see a frame rate reduction of around 400 FPS => 160 FPS for close-up planet views like in the screenshots below. This is acceptable for a single planet, as 160 FPS is still good enough for my graphics card. This could be a problem on lower end cards though, or in cases where multiple planets are very close together. Maybe I need to add some sort of graphics quality option to enable this new mode.

Here are some screenshots. Note that planets are drawn from vertex data forming simple spheres. Planets with atmosphere are drawn as flat spheres in multiple passes for the atmosphere, clouds, and terrain+water. Other rocky planet types use a tessellation shader to perturb the sphere vertices, producing a true 3D surface heightmap. I found that perturbing the heightmap caused problems with atmosphere rendering, which is why it's disabled for planets with atmosphere.

Procedural Earth-like planet with atmosphere, oceans, clouds, and ice caps. A small moon is visible on the left.

Closeup of the planet showing normal mapped terrain, cloud shadows, atmospheric lighting, and specular ocean reflections.

Here's another planet with multiple moons. This one is larger, with a thinner atmosphere and cloud layer, but higher noise frequency and more continents. All of these parameters are procedurally generated, producing quite a variety of planets. I chose to show Earth-like planets because the contrast between land and water makes it easier to see the noise patterns.

Another larger procedural planet with smaller oceans, lighter cloud cover, and thinner atmosphere.

Procedural moon with some water. A planet and another moon are seen in the background.

A third Earth-like planet. The bright area of ocean looks like the Gulf of Mexico, but it's actually procedurally generated.

This was a pretty simple change, though it required a few hours of parameter tweaking to make it both good looking and fast. I did run into some problems with floating-point precision - or rather texture lookup precision in the fragment shader. The problem was with the lower noise octaves where a large portion of the planet's surface mapped to a single texel of the noise texture. Apparently GPU texture sample points and interpolation use 8-bit fixed point math, even if using a 16-bit or floating-point texture. This took me hours to figure out. This appears to be a hardware limitation of the texture samplers and not something that's well documented or easily fixable.

In the end I had to work around the problem by adding manual texel interpolation to my shader. This made it much slower as it increased the number of texture reads by a factor of 4. I recovered some of the performance loss by reducing the number of noise octaves from 8 to 4 for the domain warp texture lookup. I was also able to get away with only using this manual interpolation for domain warping and keeping the faster hardware texture interpolation for the main noise function, where it made less of a difference on image quality. Overall, I feel the increase in quality justifies the reduction in framerate.