Cube to Sphere Terrain Projection

Because a few people who read the blog/website have asked about how to convert a cube into a sphere when creating planets, I have decided to write this entry.

Let me just outline the goal of this post:

We want to create a planet mesh, which we can render with LOD, Culling and it should be straight forward to place objects on the ground (trees, buildings etc. etc.)

Usually when rendering terrains, I find people tend towards using Quadtree Spatial Partitioning.  This is a very good method, but it is limited in that the terrain (or plane) it partitioned along 2 axis’ only, e.g the X and Z axis (Quadtrees are explained in the post here).  The difference for us is that we will then need to define 6 quadtrees, one for each side of a cube, which means orientating each plane differently (depending on which side of the cube they are, their local X and Z axis’ will be pointing in different directions in world space). By using several Quadtrees, we will have gained the ability to use LOD when rendering, and we can also then cull all the nodes which are not visible by the camera.  We can also insert objects easily into quadtrees, and by providing the proper quadtree index (in the [0,5] range), we can specify on which side of the planet the objects are.

But just using quadtrees doesn’t actually solve the problem completly, as we still need to convert the cube to a sphere – but this part is quite easy, as you are about to see.

Part I – Defining the six quadtree sides, and creating the cube.

// Creates the planet faces.
public void BuildPlanet()
{
// Calculate the max LOD based on the radius of the planet.  For a planet the size of earth,
// the liLODMax is calculated to 46.
int liLODMax = (int)Math.Log((float)((2.0f * Math.PI * m_fRadius) / 4.0f), 2);// Now calucate the size of each cube (planet face).  We will use this variable when defining the world space

// coordinates of the vertices.
int liCubeSize = m_iLODMax * 2;
int liHalfCube = m_ liCubeSize / 2;

// Create and build quadtree face #0
//                                 cWorldNode(int index, int maxLOD, cWorld parent)
m_Faces[0] = new cWorldNode(0, liLODMax, this);// Now call the function which will create the actual mesh data for this quadtree/planet face.

//        Game game - the current instance of the game class
//        Vector3 orientation - This vector defines which side of the planet cube this quadtree should represent.
//            Two of the components should be 0, and the other component should be liHalfCube;
//              Vector3 localX - The 'direction' of the local X axis in world space.
//              Vector3 localZ - The 'direction' of the local Z axis in world space.
m_Faces[0].BuildParentNode(Game, new Vector3(0, 0, liHalfCube), new Vector3(m_ liCubeSize, 0, 0), new Vector3(0, m_ liCubeSize, 0));

// Create the other 5 planet faces /* code removed for space */

}

So, you should be able to see from the above code, that we have just created the first Quadtree plane for the positive Z-Axis of the cube.  This will of course need to be done another 5 times for the other sides, but this should be no problem.

The next part (part II) takes place within the BuildParentNode() function, and it is here that we actually create the mesh, assign texture coordinates, weights etc. (and where we will transform the cube to a sphere).

Part II – Terrain Node Creatation

/// <summary
/// Builds the parent node information for this quadtree face.  The actual terrain mesh is generated in this function.
/// Note that for child nodes, there is a separate function, BuildChildNode().
/// </summary>
/// game is the current instance of the XNA game class.
/// centre is the centre of the planet face (on one of the cube edges).
/// dx is the direction of the X-Axis in local 'terrain space'.
/// dy is the direction of the Y-Axis in local 'terrain space'.
Public void BuildParentNode(Game game, Vector3 centre, Vector3 dx, Vector3 dy)
{

/*  code removed for vertex buffer generation*/

// Define the grid size.  i.e. the number of vertices in a grid (16x16)
int liGridSize = 16;
int liGridSizePlus = 17; // liGridSize + 1

// loop through and create a grid of vertices.
for (int u = 0; u <= liGridSize; u++)
{
for (int v = 0; v <= liGridSize; v++)
{
// Create the vertex grid around the centre of thecube face (which is passed into the function as Vector3 centre).
Vector3 tempPosition = centre + (dx / liGridSize) * (v - liGridSize / 2) + (dy / liGridSize) * (u - liGridSize / 2);

// This is where we would define the height of the vertex.
lfHeight = 0;

// Project the vertex onto the sphere, taking into consideration the height of the
// vertex and the radius of the planet.  By specifying 0 as the height, we will
// get a 'perfectly' round planet/sphere.
m_vVertexPositions[liGridSizePlus * u + v] = SurfaceVectorToCoordinates(tempPosition, m_Parent.Radius, lfHeight);

/* code removed for space */
/* Assign texture coordinates and weights etc*/
}
}

/* code removed for space */
/* build the vertex buffer and create a bounding box for this terrain node */
}

/// <summary>
/// Transforms a vector from the surface of a cube, onto the surface of a sphere.
/// </summary>
/// surfacePos is the vertex position on the cube.
/// radius is the radius of the planet.
/// height is the height of the terrain at this position.
public static Vector3 SurfaceVectorToCoordinates(Vector3 surfacePos, float radius, float height)
{

// Create a return veriable.
Vector3 loReturnData = surfacePos;

// Get a unit vector ( this will 'point' in the correct direction, from (0,0,0) to
// the position of the vertex on the sphere ).
loReturnData.Normalize();

// Add the planet radius and the height of the vertex, and return the vector.
return loReturnData *(radius + height);
}
A lot of code has been removed from the above function, but it should now at least be clear how I have created the planets in Britonia.

Have fun!

// Creates the planet faces.
public void BuildPlanet()
{
// Calculate the max LOD based on the radius of the planet.  For a planet the size of earth,
// the liLODMax is calculated to 46.
int liLODMax = (int)Math.Log((float)((2.0f * Math.PI * m_fRadius) / 4.0f), 2);// Now calucate the size of each cube (planet face).  We will use this variable when defining the world space

// coordinates of the vertices.
int liCubeSize = m_iLODMax * 2;
int liHalfCube = m_ liCubeSize / 2;
// Create and build quadtree face #0
//                            cWorldNode(int index, int maxLOD, cWorld parent)
m_Faces[0] = new cWorldNode(0, liLODMax, this);// Now call the function which will create the actual mesh data for this quadtree/planet face.

//        Game game – the current instance of the game class
//        Vector3 orientation – This vector defines which side of the planet cube this quadtree should represent.
//            Two of the components should be 0, and the other component should be liHalfCube;
//        Vector3 localX – The ‘direction’ of the local X axis in world space.
//        Vector3 localZ – The ‘direction’ of the local Z axis in world space.
m_Faces[0].BuildParentNode(Game, new Vector3(0, 0, liHalfCube), new Vector3(m_ liCubeSize, 0, 0), new Vector3(0, m_ liCubeSize, 0));
// Create the other 5 planet faces /* code removed for space */
}

Advertisements
  1. #1 by Andrew Ryan on March 8, 2010 - 6:40 am

    Um, what exactly is the point of this? I cannot use this as a tutorial as there is far too much missing. You provide no samples either. So all you have here is a bunch of bragging that you have some cool stuff working.

    On the internet, space is free when you are dealing with text, so why cut it out?

    You might as well take this down otherwise.

    • #2 by Lintfordpickle on March 8, 2010 - 8:35 am

      This isn’t a tutorial, it is an article for cube->sphere projection of vertices. The article (and code) just show how one can define a cube using 6 grids of vertices and then push each vertex outwards to look like a sphere.

      The reason for wanting to do this is that the planet (and objects on the surface) can then be managed just like a normal quadtree. Furthermore, using quads as the planet faces mean texturing and creating seamless heightmaps is easier.

      There are many sources of infomation and even tutorials for rendering planets on the internet, and my site isn’t the only place to look. But if you are looking for a planet rendering tutorial which is more than just textured spheres, then I’m afraid you’ll be disappointed. For great references I could recommened vterrain.org. Or, if you have any specific questions, just ask.

      -John

  2. #3 by Andrew Ryan on March 11, 2010 - 4:24 am

    Point taken. Disregard first comment, it is stupid and just frustration talking. This is the best laid out bit of info on the subject I have found.

    Sorry.

    • #4 by Lintfordpickle on March 11, 2010 - 10:28 am

      no worries,

      but if you do have a specific question, feel free to post it (maybe in the forums) and I’ll try and help if I can. Failing that, there are others with experence of planetary renderers who may be able to help.

      -John

  3. #5 by Erik on March 23, 2010 - 8:49 pm

    Hi, really impressive engine you got going. What technique are you using for making the textures/height map on each side fit together?

    • #6 by Lintfordpickle on March 23, 2010 - 8:54 pm

      hi Erik,

      its just 3d noise (ridgemf and fBm) using the vertex positions as input. This way, because two neighbouring vertex grids share the same edge (with the same coordinates), the edges of the heightmaps line up.

  4. #7 by Erik on March 24, 2010 - 8:09 pm

    Ah, of course, was over thinking it 🙂 thanks

  1. Creating a Planet : Geometry :

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: