Procedural Planets

Normal-mapped planet.

Normal-mapped planet.

The semester is over at last, and my grades should be in by Monday. It has been a tiring semester, but not a bad one, with interesting courses in database implementation and parallel architectures, not to mention philosophy.

With the end of the semester comes a little more free time, and I have spent a chunk of it recreating my old procedural planet demo in Python/Pyglet.

The first big plus is that whereas my previous implementation was over 5,000 lines of C++ code, the python version is under 1,000 loc, plus a few hundred lines of tightly optimised C for the tile generation back end.

The other plus is that this version actually works. I finally found the time to fully implement the crack fixing algorithm, and the results are very good (although there are still a couple of unresolved edge cases).

I also implemented normal map generation in GLSL, to offload the computation to the GPU. This more than doubles the performance of tile generation, to the point where several tiles can be subdivided or combined each frame.

Wireframe of planet.

Wireframe of planet.

From a technical standpoint, the planet starts as a cube, and each face is subdivided to form a quad tree. For each tile at the deepest level of the quad tree, a small heightmap is generated using 32 octaves of 3-dimensional simplex noise. This heightmap is used to generate the vertices for the tile, and passed as a texture to the GPU in order to generate the normal map.

Because applying tangent-space normal maps to a sphere is an absolute nightmare, I take the unusual approach of generating object-space normal maps. These are considerably more expensive to generate, but avoid the tangent-space mismatch at cube edges, and look fairly decent in practice.

Interestingly, this version allows one to fly right down to the planet surface, and maintains interactive framerates even on my Intel integrated X3100 (complete with the awful Mac drivers). By the time I add atmosphere shaders and detail textures, I expect that I will have to switch over to my desktop, but for now, I am very happy with the performance.

Of course, there are still several challenges to overcome, in particular the issue of depth buffer precision. The planet you see above does not suffer from a lack of depth buffer precision, but it is only 1/10 scale of the Earth, and ideally I would like to be able to render gas giants on the order of Jupiter as well. This requires some clever on-the-fly adjustment of the camera frustum, and I don’t quite have a handle on the best way to accomplish it.


4 comments

  1. Nice work! What brought your # of lines of code down so much moving from c++ to python?

    Also, do you care to elaborate on your algorithm to generate normals on the GPU? i.e. are you generating the normals by smoothing triangle normals, or are you calculating the normals directly from the 2D heightmap like the GPU gems 3 algorithm does and then transforming those to sphere space?

  2. Hi, came across your gamedev posts regarding polygonisation.
    Could you post some reference implementation of using Dual methods? Though on your screenshots it looks like you are not stitching cracks, is this correct?

    Thanks!


Leave a comment