misc, programming

Equirectangular Maps to Cube Maps

A solution to a mildly tough problem

On my last blog, I wrote about a way to generate worlds, and for that, I suggested using Cube Maps to store the data and then to convert to Equirectangular to display the map.
Converting to Equirectangular, thanks to Bartosz simple and beautiful to understand code, it is also easily convertible to other languages and can be modified to output either an entire set of coordinates or just a single coordinate.
However, reversing this algorithm is not so simple, since the maths aren’t reversible and thus we are forced to search for a different solution.

Salix alba’s solution to this problem is fantastical to convert an entire image, and the code can also be easily copy-pasted, however, the way the algorithm works doesn’t allow for what I truly needed: a way to convert single coordinates from one coordinate system to the other.

Understanding the various coordinate systems

When making the conversion from Equirectangular to Cube Map, we deal with multiple coordinate systems:

  • Equirectangular Coordinates – Also called Cylindrical Equidistant Projection, it’s aspect ratio is always 2:1, it uses the (x,y) coordinate system. It represents a projection of a planet.
  • Spherical Coordinates – Spherical coordinates imply two kinds of coordinates: latitude and longitude(or phi and theta). There is a third one, radius, but for our purposes, it doesn’t matter. They represent a spherical space or in our case, the globe.
  • Cartesian Coordinates – Cartesian coordinates make use of (x,y,z) coordinates. They represent a 3D space.
  • Cube Map Coordinates – Cube Maps go back to using only an (x,y), but to avoid confusion, let’s call it (u,v).

From Equirectangular to Spherical

When converting to spherical coordinates, we want to obtain the latitude and longitude of a determined pixel on the map. The calculations to achieve that is easy, the method is explained on Wikipedia and on Mathworld.
Which leaves us with this pseudo code whose output is in radians:

function calculatePhi(y, height) {
  return (1-2*y/height)/2.0 * Math.PI;
}
  
function calculateTheta(x, width) {
  return (2*x/width-1) * Math.PI;
}
The output, when converted to degrees follows this standard. (A latitude of 90 degrees will represent the north pole, etc)

From Spherical to Cartesian

Converting from spherical to cartesian is pretty simple, Wikipedia has a pretty good guide on it, as well as Paul Bourke in his blog post about this same theme.

function calculateX(theta, phi) {
  return Math.cos(phi) * Math.cos(theta);
}
  
function calculateY(phi) {
  return Math.sin(phi);
}
  
function calculateZ(theta, phi) {
  return Math.cos(phi) * Math.sin(theta);
}
All credits to SEOS for this image showcasing what’s happening during this conversion

Cartesian to Cube Map

This is the hardest conversion, thankfully, once again, Wikipedia has got our backs, however, this time the algorithm isn’t fully complete, instead, we need to add the last function to convert from local square coordinates to the “horizontal cross” shape we want.

This is due to the nature of the way the Cartesian to CubeMap is done by Wikipedia’s algorithm, which outputs a normalized result.

function localToGlobalCoords(coords, index, cubeFaceSize) {
    switch (index)
    {
        case 0:// POSITIVE X - FRONT
            coords[0] = cubeFaceSize*2 - coords[0]*cubeFaceSize;
            coords[1] = cubeFaceSize*2 - coords[1]*cubeFaceSize;
            break;	
        case 1:// NEGATIVE X - BACK
            coords[0] = cubeFaceSize*4 - coords[0]*cubeFaceSize;
            coords[1] = cubeFaceSize*2 - coords[1]*cubeFaceSize;
            break;	
        case 2:// POSITIVE Y - TOP
            temp = coords[0];
            coords[0] = cubeFaceSize*2 - cubeFaceSize*coords[1];
            coords[1] = cubeFaceSize*temp;
            break;	
        case 3:// NEGATIVE Y - BOTTOM
            temp = coords[0];
            coords[0] = cubeFaceSize - cubeFaceSize*(-coords[1]);
            coords[1] = cubeFaceSize*3 - cubeFaceSize*temp;
            break;	
        case 4:// POSITIVE Z - RIGHT
            coords[0] = cubeFaceSize*3 - coords[0]*cubeFaceSize;
            coords[1] = cubeFaceSize*2 - coords[1]*cubeFaceSize;
            break;	
        case 5:// NEGATIVE Z - LEFT
            coords[0] = cubeFaceSize - coords[0]*cubeFaceSize;
            coords[1] = cubeFaceSize*2 - coords[1]*cubeFaceSize;
            break;	
    }
    return coords;
}

Essentially, what is happening here is that we are projecting the points that are sitting in a sphere, and projecting them onto a square.

Huge thanks to proj4.org for this image

Closing thoughts

Converting between coordinate systems can be complicated, especially if we don’t know exactly what we are dealing with. My solution is by no means a perfect solution to this problem, if your main goal is to convert an entire image then I highly recommend you use Salix alba’s solution instead.

What I wanted was to convert a single point from Equirectangular to Cube Map, and although Salix Alba’s solution is quite amazing, it simply did not fit the bill for what I wanted, which forced me to look for other options.

2 thoughts on “Equirectangular Maps to Cube Maps

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 )

Connecting to %s