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;
}
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);
}
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.
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.