using GeoData, surface plot, and a bunch of other beautiful stuff
Have you ever played Outer Wilds
? Planets there are so beautiful. That was the main motivation to rebuild one using real elevation data and a bunch of WL's magic
Spreading points on a sphere
A small task to do
Graphics3D[{ Sphere[], Red, PointSize[0.01], SpherePoints[1000]//Point }]
(*VB[*)(FrontEndRef["6133ae90-57e8-4be5-8ed4-54d14e466715"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKmxkaGyemWhrompqnWuiaJKWa6lqkppjompqkGJqkmpiZmRuaAgB9OhUh"*)(*]VB*)
Convert to Geo coordinates
Find latitude and langitude
points = SpherePoints[5000]; latLon = (*FB[*)((180)(*,*)/(*,*)(Pi))(*]FB*) {90Degree - ArcCos[#[[3]]], ArcTan[#[[1]], #[[2]]]} &/@ points; elevation = GeoElevationData[GeoPosition[latLon]]; elevation = (elevation + Min[elevation])/Max[elevation];
In the case if Geo
did not work. Here is a dump
{points, latLon, elevation} = (*VB[*)(Get[FileNameJoin[{".iconized", "iconized-337e.wl"}]])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRAeH5DwTM7Py6xKdcvMSXXKr8j0YOVmAADrbgrN"*)(*]VB*);
rainbow = ColorData["DarkRainbow"]; ListSurfacePlot3D[MapThread[(#1 (0.8 + 0.1 #2))&, {points, elevation}], Mesh->None, MaxPlotPoints->100, ColorFunction -> Function[{x,y,z}, rainbow[1.5(2 Norm[{x,y,z}]-1)]], ColorFunctionScaling -> False]
(*VB[*)(FrontEndRef["ef4101c8-35c1-415b-a881-83c0e3a8f8f1"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKp6aZGBoYJlvoGpsmG+qaGJom6SZaWBjqWhgnG6QaJ1qkWaQZAgB+RxVt"*)(*]VB*)
That looks like Earth!
Generating clouds
One can simply use Perlin noise and perform marching cubes
n = 128; k2 = Outer[Plus, #, #] &[RotateRight[N@Range[-n, n - 1, 2]/n, n/2]^2]; spectrum = With[{d := RandomReal[NormalDistribution[], {n, n}]}, (1/n) (d + I d)/(0.002 + k2)]; spectrum[[1, 1]] *= 0; im[p_] := Clip[Re[InverseFourier[spectrum Exp[I p]]], {0, ∞}]^0.5 p0 = p = Sqrt[k2]; Image[im[p0 += p]]
(*VB[*)(FrontEndRef["f77ace65-5c0e-4968-abdf-b65fdb70367f"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKp5mbJyanmpnqmiYbpOqaWJpZ6CYmpaTpJpmZpqUkmRsYm5mnAQCRdxZG"*)(*]VB*)
It is hard to wrap on the sphere RegionMesh
function from the standard library. Therefore we will go with an external library
PacletRepositories[{ Github -> "https://github.com/JerryI/wl-marching-cubes" -> "main" }] <<JerryI`MarchingCubes`
Now generate vertices and map them to sphere
With[{plain = im[p0+=p]}, Table[plain Exp[-( i)^2/200.], {i, -20,20}]]; {vertices, normals} = CMarchingCubes[%, 0.2, "CalculateNormals" -> False]; vertices = Map[Function[v, With[{\[Rho] = 50.0 + 0.25 (v[[3]] - 10), \[Phi] = 2.0 Pi v[[1]]/127.0, \[Theta] = Pi/2 + Pi v[[2]]/127.0}, {\[Rho] Cos[\[Phi]] Cos[\[Theta]], \[Rho] Sin[\[Phi]] Cos[\[Theta]], \[Rho] Sin[\[Theta]]} ] ] , vertices]; { clouds = GraphicsComplex[0.017 vertices, Polygon[1, Length[vertices]]] } // Graphics3D;
We shifted angle for purpose, to avoid visibles artifacts on poles.
Combine
Plot together
rainbow = ColorData["DarkRainbow"]; lightPos = {-2.4909, 4.069, 3.024}; rotationMatrix = RotationMatrix[0., {0,0,1}]; angle = 0.; animation = CreateUUID[]; EventHandler[animation, Function[Null, lightPos = RotationMatrix[1 Degree, {1,1,1}].lightPos; rotationMatrix = RotationMatrix[angle, {0,0,1}]; angle += 0.5 Degree; ]]; ListSurfacePlot3D[ MapThread[(#1 (0.8 + 0.1 #2))&, {points, elevation}], Mesh->None, MaxPlotPoints->100, ColorFunction -> Function[{x,y,z}, rainbow[1.5(2 Norm[{x,y,z}]-1)]], ColorFunctionScaling -> False, Lighting->"Default", PlotStyle->Directive["Shadows"->True, "CastShadow"->True], Prolog -> { Directive["Shadows"->True, "CastShadow"->True], GeometricTransformation[clouds, rotationMatrix // Offload], HemisphereLight[LightBlue, Orange // Darker // Darker], SpotLight[Orange, lightPos // Offload] }, Epilog -> AnimationFrameListener[lightPos // Offload, "Event"->animation], Background->Black ]
(*VB[*)(FrontEndRef["c8e48c06-39cf-41b3-b9e8-9d005bb356f5"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKJ1ukmlgkG5jpGlsmp+maGCYZ6yZZplroWqYYGJgmJRmbmqWZAgCHGhWu"*)(*]VB*)
To start animation
EventFire[animation, True];