First of all you can avoid all the hassle by simply using Google Maps in 3D mode. Here’s a view on the Eifel tower in paris (it’s a google.de link since google aggressively prevents you to use google.com if you are located in germany so hopefully it opens as intended). Note that the eiffel tower has an height of about 300m. If you zoom out you shouldn’t see any curvature at all until you are really far out. Unfortunately google maps doesn’t tell you your view height, though you can approximately deduce the height in relation to the tower.
Apart from that i achieved a similar result in Unity. However i used a scale of 0.001. So one unit becomes 1000m. I actually implemented a crude straight forward raytracer for rendering the earth. I simply used Unity’s SphereCollider(radius 6000, position (0, -6000, 0)) and Collider.Raycast to do the ray tracing. That means the world origin (0,0,0) is just at the surface of the sphere. The raytracing looks like this:
void UpdateTex()
{
UpdateRes();
var cam = Camera.main;
var col = GetComponent<SphereCollider>();
var center = transform.position;
for (int y = 0; y < m_Height; y++)
{
int yOff = y * m_Width;
for (int x = 0; x < m_Width; x++)
{
Ray r = cam.ScreenPointToRay(new Vector3(x, y));
RaycastHit hit;
Color c = Color.red;
if (col.Raycast(r, out hit, float.PositiveInfinity))
{
var dir = (hit.point - center).normalized;
dir = transform.InverseTransformDirection(dir);
// local lat / lon
var l0 = (Mathf.Asin(dir.y) / Mathf.PI) + 0.5f;
var l1 = Mathf.Atan2(dir.z, dir.x) / (Mathf.PI*2);
// sample texture
c = earthTex.GetPixelBilinear(l1, l0);
// apply simple directional lighting
c = c * Vector3.Dot(hit.normal, -light.forward);
c.a = 1;
}
else
c = Color.clear;
m_Colors[yOff + x] = c;
}
}
tex.SetPixels(m_Colors);
tex.Apply(false);
}
The “UpdateRes” method just sets up the texture so it matches the screen resolution as well as creating the Color array (m_Colors).
As texture i used the blue marble (highest resolution), though it’s just to provide some structure to the surface. I placed a 1m large cube (scale of 0.001) at (0, 0.0005, 0). The camera uses a near clipping plane of “0.01” (you can’t set it lower in Unity) and a far clipping plane of 1000, though the clipping planes have no meaning to the raytracer ^^. Here’s an animated gif as the camera is moving straight up in steps:

Note that the “white thing” in the first two images is the 1m cube which is about 50m in front of the camera. The ray tracer will render just the earth into a texture which is then blitted to the screen as “background” using a seperate camera. Of course the real time performance is horrible. At this small resolution, when the whole sphere is in view i get about 7 frames per second ^^. If the sphere is completely off screen i get 26 fps (since no texture lookup and no lighting calculation takes place, just about 70000 raycasts and the texture update)
As i use a scale of 0.001 that means when the camera is at y= 0.01 we are at 10m height above ground. “0.1” would be 100m and “1.0” would be 1000m. The sphere is a perfectly round sphere since we just raycast against a sphere collider to render that image.
ps: the location on earth is just an arbitrary location. I just rotated the earth 45° on x to actually have land below and not just ice ^^. I also just realised even i used the 8k earth texture it had it’s default max size of 2k . Though the difference it’s that huge at close distance since at 8k we only have 0.38 pixels per seamile. (btw. the seamile is the only length measurement we use on earth that actually makes sense ^^)