Home

Making Worlds: 1 - Of Spheres and Cubes

Aug 23, 2009

Let's start making some planets! Now, while this started as a random idea kind of project, it was clear from the start that I'd actually need to do a lot of homework for this. Before I could get anywhere, I needed to define exactly what I was aiming for.

The first step in this was to shop around for some inspirational art and reference pictures. While there is plenty space art to be found online, in this case, nothing can substitute for the real thing. So I focused my search on real pictures, both of landscapes (terran or otherwise) as well as from space. I found classy shots like these:


Hopefully I'll be able to render something similar in a while. At the same time, I eagerly devoured any paper I could find on rendering techniques from the past decade, some intended for real-time rendering, some old enough to be real-time today.

Out of all this, I quickly settled on my goals:

  • Represent spherical or lumpy heavenly bodies from asteroids to suns.
  • With realistic looking topography and features.
  • Viewable across all scales from surface to space.
  • At flight-simulator levels of detail.
  • Rendered with convincing atmosphere, water, clouds, haze.

For most of these points, I found one or more papers describing a useful technique I could use or adapt. At the same time, there are still plenty of unknowns I'll need to figure out along the way, not to mention significant amounts of fudging and experimentation.

The spherical grid

To get started I needed to build some geometry, and to do that I needed to figure out what geometry I should use. After reviewing some options, I quickly settled on a regular spherical displacement map (AKA a heightmap). That is, starting with a smooth sphere, move every surface point up or down, perpendicular to the surface, to create terrain on the surface.

If these vertical displacements are very small compared to the sphere radius, this can represent the surface of a typical planet (like Earth) at the levels of detail I'm looking for. If the displacements are of the same order as the sphere radius, you can deform it into very irregular potato-like shapes. The only thing heightmaps can't do is caves, tunnels, overhang and other kinds of holes, which is fine for now.

The big question is, how should the spherical surface be divided up and represented? With a sphere, this is not an easy question, because there is no single obvious way to divide a spherical surface into regular sections or grids. Various techniques exist, each with their own benefits and specific use cases, and I spent quite some time looking into them. Here's a comparison between four different tesselations:

Note that the tesselation labeled ECP is just the regular geographic latitude-longitude grid.

The main features I was looking for were speed and simplicity, so I settled on the 'quadcube'. This is where you start with a cube whose faces have been divided into regular grids, and project every surface point out from the middle to an enclosing sphere. This results in a perfectly smooth sphere, built out of 6 identical round shells with curved edges. This arrangement is better known as the 'cube map' and often used for storing arbitrary 360 degree panorama views.

Here's a cube and its spherical projection:

Mapping a cube to a sphere
The projected cube edges are indicated in red. Note that the resulting sphere is perfectly smooth and round, even though the grid has a bulgy appearance.

Cube maps are great, because they are very easy to calculate and do not require complicated trigonometry. In reverse, mapping arbitrary spherical points back onto the cube is even simpler and in fact natively supported by GPUs as a texture mapping feature.

This is important, because I'll be generating the surface terrain and texture dynamically and will need to index and access each surface 'pixel' efficiently. Using a cube map, I simply identify the corresponding face, and then index it using x/y coordinates on the face's grid.

The downside of cube maps is that the distance and area between points varies along the grid, which makes it harder to perform certain operations on a surface equally. However, these area distortions are much smaller than e.g. a lat-long grid, where the grid spacing actually approaches zero near the poles. Even more, the distortions made by a cube map are the exact opposite of those you get with a regular perspective projection. This makes it easy to render into cube maps, which will be useful for texture generation.

Level of detail

There's another reason I picked the cube map approach, and that has to do with the level of detail requirements. My goal is to make a planet that can be viewed from the ground, the air as well as from space. It would be incredibly slow to always render everything at maximum detail, so I need to adaptively add and remove detail as the viewer gets closer to the surface.

However, increasing the level of detail uniformly across the entire sphere is not enough, because I only want to render detail where the viewer will see it. To a viewer on the ground, most of the planet is hidden by the horizon, and the engine should be able to effectively cut away the unseen pieces, so no wasteful processing takes place.

It is here that I get a huge benefit from the cube map layout of the sphere, because it lets me apply the well-researched realm of grid-based flat terrain rendering with only minor adjustments. Specifically, I am using a 'chunked LOD' approach. Every face of the cube map becomes a quadtree, with each level splitting four ways to form the next level with more detail:

The chunks for the various levels of detail chunks are all loaded into GPU memory, ready to be accessed at any time. When the terrain has to be rendered, the engine walks down the quad-tree, determines the appropriate level-of-detail for each section, and outputs the list of chunks to be rendered for a particular frame. Then, the GPU does its work, blasting through each chunk at a blistering pace, leaving the CPU to do other things.

Because all the data is already in memory, changing the level of detail just means rendering a different set of chunks. Each chunk has the same geometrical complexity, and performance is directly proportional to how many are rendered on screen. More detail means more chunks, but that usually also means you can cut away pieces of the terrain that are far away.

The chunked approach is also very easy to work with, because there is no data dependency between the different chunks. Each chunk has a copy of its own vertex data, which means individual chunks can be paged in and out of GPU memory at will. This is important for keeping memory usage down while still being able to scale to massive sizes.

Putting it all together

At this point, I have all the pieces in place to render an adaptive sphere mesh. This is what it looks like (sorry, the video capture is a bit jerky):

The detail increases as the camera gets closer to the sphere and shifts around the surface as it moves.

Far from being a little coding experiment, it actually took me quite some time to get to this point, because I was learning OGRE, sharpening my C++ skills, as well as researching the techniques to use.

The next step is to look at generating heightmaps and textures for the surface.

References

The techniques I used were pioneered by people smarter and older than me, I'm just building my own little digital machine with them.

Previous: Introduction
Next: 2 - Scaling Heights

Cool...

Aug 27, 2009 Semi Essessi

I hope you finish this one... its something I've had a few stabs at myself on previous gen hardware, but typically gave up on it. :)

I look forward to the next post in this series...

Sweet

Aug 29, 2009 Andy

Nice! I'm reminded of the old Tribes (multiplayer 3d shooter) engine - as you moved about, the terrain detail would enhance; the surface would "tween" from less polygons to more, causing a noticeable morph in the terrain as you approached. Anyway, I thought I would share these links for texturing your planet; they are probably the best [free] textures of our own solar system:

Earth:
http://earthobservatory.nasa.gov/Features/BlueMarble/BlueMarble_monthlie...

Other planets/moons:
http://www.nasa.gov/multimedia/3d_resources/2d-images-gallery.html

@Andy: Thanks

Aug 29, 2009 Steven

Thanks, Andy, those are great references that I wasn't aware of yet. That said, my goal is to be able to generate such textures out of thin air myself ;).

I've been working on accurate level-of-detail control, and though I haven't actually added morphing between detail levels, it isn't that complicated to add. Ultimately though, the detail is completely tuneable, so on a beefy enough machine, you could in theory run terrain engines like this at such detail levels so that they never need morphing at all.

Sphere points to cube

Oct 01, 2009 Phil

Hi Steven,
I've been working on my own planetary renderer/generator using pretty much your same techniques, but I have been stumped about mapping arbitrary points from the sphere back onto the cube. You mentioned GPUs natively supporting this operation, but I am relatively new to high level shader languages. Would you mind telling me what function this is? Or perhaps you know the inverse math for the sphere->cube?

Thanks a lot!

Mapping

Oct 01, 2009 Steven

The mapping comes down to simple vector scaling. In one direction it's normalization, in the other it's scaling by the inverse of the largest co-ordinate.

See:
http://developer.nvidia.com/object/cube_map_ogl_tutorial.html

Cubed-Sphere Tip

Nov 06, 2009 Erlend

Hi Steven,

Im using the cubed-sphere approach myself for an Earth LOD algorithm, but instead of normalizing the vectors to go from cube to sphere, i use the formula described here: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html, which creates more equal sized triangles.

Also you could probably do with only ONE set of vertices for ALL the tiles, and transform them in the vertex shader. Not sure how it would perform though, but you would save memory.

@Erlend: Thanks for the suggestions

Nov 06, 2009 Steven

I'm not too concerned about equal area between the triangles, because the LOD is determined by the geometry of the underlying the heightmap. So in the corners (where the triangles are relatively smaller), it will automatically prefer a lower LOD than in the middle.

Given the complexity of the formulas you described, it sounds like drawing into a cubemap with such a projection would be a lot trickier, and deriving the normal map even harder (see part 3 of this series). Right now, I prefer simplicity.

As for doing the transforms in a vertex shader, it's on my list of things to try. It turns out my original estimates for memory requirements were wrong for two reasons:

  • The smallest pixel format that supports signed quantities, needed for proper brush carving, is FLOAT16 (at least on Ogre).
  • Render targets on consumer GPUs cannot be monochrome, but must be in RGB or RGBA format.

That means that even a 1024x1024x6 cubemap takes up 37MB, and going to 2048x2048x6 is prohibitively expensive, as I need at least two cubemaps at any time to be able to apply filtering operations to the data.

Reply

Nov 07, 2009 Erlend

The formulas are a bit more tricky but i need it and i dont think its the major speedbump. Im also converting the unit length cartesian coordinates on the sphere to lat lon and then to WGS-84 earth coordinates. Im sure I could optimize the calculation some..

I'm not using a cube map for texturing, and I dont have much experience with it. Instead i have a 2d texture mapped to each node/tile of the quad tree, which is preprossesed and which i load on demand. So im using quite a bit of texture memory, since i create and keep all the textures (and vertices), from root to the level Im at. 118 tiles loaded => 512*512*4*118 == 118 MB. And thats without normal textures. .Im not sure how you do the LOD for textures.. Do you have one cube mapped texture per quadtree level? If so, isn't that expensive, since you have to create a new cube map for each time you increase the LOD level?

Math and refinement

Nov 07, 2009 Steven

In my case, I'm calculating the texture and its normal map on the fly, which requires accurate mapping of coordinates, including derivatives. Those might get a bit bulky for your altered formulas.

As for the refinement, I'm still working on that, but my current plan is to generate a global 1024x1024 cube map for the surface, and then generate refined versions of individual (squarish) tiles on the fly, at various scales. These don't have to be cubemapped at all. I don't need to generate or load a high-res version of the entire surface at the same time, because the camera will be low enough to clip off tiles beyond the horizon.

Nice!

Mar 12, 2010 Gregory Magarshak

Wow, dude. I used to be into 3d rendering, including terrains and fractal surfaces and all that. Amazing that you tackle all this out of an interest in graphics and design.

http://flipcode.com/tpractice/ if you're curious

Post new comment

Note: all posts containing spam will be removed.
The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <b> <dd> <dl> <dt> <i> <li> <ol> <u> <ul> <img> <em> <p> <br> <span> <div> <h2> <h3> <abbr> <small> <table> <tr> <td> <strong> <acronym> <th> <blockquote>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

Recent comments

Images