# Unity3d - Tile-based "Slippy" map rendered on a sphere (earth)

Scenario

I'm creating a spherical version of a slippy map using osm tiles and my luck has been limited thus far. I'm starting to think my strategy has lead me astray and that I need to start from scratch.

Current Progress

• Like the mentioned slippy maps I'm only loading tiles the user is interested in, using camera position & rotation to decipher X,Y of tiles needed
• Tiles are being positioned correctly on a sphere
• Tiles are partially rotated correctly (using basic normals)
• I'm currently working on sizing the tiles based on Y coordinate of image
• Will need to correctly position vertices for tiles as tiles close the poles need to be thinner and tiles near the equator need to be fatter
• Will need to bend tiles to match curvature of earth

Question

Is there a shader or some sort of math that already exists for Unity so that I can plug in the X,Y of an osm tile and have that texture appear correctly on the surface of a sphere ?

** some code **

    // tilesXY is the tile count per axis which depends on zoom level
// position for 3d tiles; adding .5 to keys so the polar caps look correct
float phi = (keyY + 0.5f) / tilesXY * Mathf.PI;
float theta = (keyX) / tilesXY * (Mathf.PI * 2);
float x = Mathf.Sin(phi) * Mathf.Cos(theta);
float y = Mathf.Sin(phi) * Mathf.Sin(theta);
float z = Mathf.Cos(phi);
this.transform.position = new Vector3(x, y, -z);

// wip rotation for tiles
Vector3 pu = new Vector3(x, y, -z); // plane up
Vector3 pr = Vector3.Cross(pu, Vector3.up); // plane right
Vector3 pf = Vector3.Cross(pr, pu); // plane forward
this.transform.rotation = Quaternion.LookRotation(pf, pu);

// wip scaling of tiles
float sfsy = Mathf.Sin(keyY / tilesXY * Mathf.PI);
float tsf = scaleFactor * 0.16f * (sfsy * 0.25f) + 0.016f;
this.transform.localScale = new Vector3(tsf, tsf, tsf);


    // code for using camera position + angle to figure out what tile(s) need to be loaded

Vector3 camAngle = MainCamera.instance.AngleToTarget();

UnityEngine.Camera cam = UnityEngine.Camera.main;
bool hemiFront = cam.transform.position.x > 0 ? true : false; // top bot vs front back hemispheres @jkr
bool hemiBot = cam.transform.position.y < 0 ? true : false;

// Debug.Log(\$"{hemiFront}, {hemiBot}");
float sinX = Mathf.Sin(camAngle.y / 360 * Mathf.PI);
int centerX = (int)(sinX * (tilesInfo["tilesXY"] / 2)); // center of where camera is looking, on the surface @jkr
if (hemiFront == false)
{
centerX *= -1;
centerX += tilesInfo["tilesXY"];
}

// if (hemiFront == false) 0 = 0;
float sinY = Mathf.Sin(camAngle.x / 360 * Mathf.PI);
int centerY = (int)(sinY * (tilesInfo["tilesXY"] / 2));
if (hemiBot == true)
{
centerY *= -1;
centerY += tilesInfo["tilesXY"];
}


** extra **

Screenshot of current progress

• the tight grouping of images is the north pole
• I'm pulling from a custom set of osm so anything green is water while blue is ice
• the underlying blue is a sphere I put in my scene to help me make sure the tiles are being positioned correctly

# ** EDIT **

The shader is starting to come along

• Myself, I think I'd load all of the map tiles into a texture array, then have an indirection map that the shader can look up into when it wants to find the index of the best matching tile for a given point. Then you render a simple sphere mesh for the whole world, or even a quad, and calculate the lookup coordinates in the shader, and sample/blend the relevant tiles from the array. Of course, this is easier said than done, but I think it will be easier to manage seamlessly joining tiles in image space like this than trying to generate each one as a separate piece of geometry. Dec 2 '21 at 1:39
• @DMGregory I've been entertaining texture arrays recently so thanks for mentioning that. I need to keep in mind that at a theoretical zoom level of 23 there are 70 trillion tiles. An array size of 70 trillion hurts my brain let alone 70 trillion 2d texture instantiations. How can I used something like a texture array without sizing the array? I would need something like a texture hashmap, but I know of nothing like that. Dec 2 '21 at 17:26
• That's where the indirection map comes in. Let's say at a typical view you'd need 16 tiles to cover the screen at the desired resolution. Let's say we're also keeping in cache the 16/4=4 tiles we'd need to cover the same area at the next lod down and the 16x4=64 tiles we'd need if the player zooms in. That's < 100 tiles we might be drawing from at a frame's notice. Size your array to have that many. When you need to add a new tile, evict the least recently used one. Then update the indirection map to tell the shader which array slot to look in for that part of the sphere. Dec 2 '21 at 18:09
• If I understand you correctly, in this scenario I'd have a sort of buffer at size 100 and once I get to the 101th tile I'll evict tile 1 as it's most likely unused at that point? I was certainly planning on using a metric like this for caching the bitmap data but is this something a shader / textureArray can work with? I suppose my lack of understanding is around working with textureArrays and shaders. If I have a textureArray of 100 items that is for a zoom level with more than say 1M tiles how can I make sure the tiles are loaded into the correct spots? Maybe I'm missing your point. Dec 2 '21 at 20:33
• Comments are not for extended discussion; this conversation has been moved to chat. Dec 2 '21 at 21:10