← Back to list

The Moon’s other side. 3D + AR. How I created the Top App Store iOS app on a coffee break.

Hey folks! In one of my recent projects, I had to learn how to render the Moon in 3D with realistic lighting and make it viewable both in a…

Gennady Dmitrik in Antaeus AR · 2025-08-31 16:13 · 158 claps · 2.7 min read
#ios #ios-app-development #augmented-reality #moon #open-source
Open on Medium ↗
Wiki topics: 📱 · Mobile Development 🔓 · Open Source 🔭 · Astronomy & Space 🍳 · Food & Cooking

The Moon’s other side. 3D + AR. How I created the Top App Store iOS app on a coffee break.

Moonlight phase iOS application https://apps.apple.com/us/app/moonlight-phase/id1666801366 and source code https://github.com/ndwdm/Moonlight-ios

Moonlight phase iOS application https://apps.apple.com/us/app/moonlight-phase/id1666801366 and source code https://github.com/ndwdm/Moonlight-ios

Hey folks! In one of my recent projects, I had to learn how to render the Moon in 3D with realistic lighting and make it viewable both in a standard 3D scene and in AR mode. Along the way, I experimented with AVPlayer, SceneKit, ARKit, and some tricks for lighting and shadows that make the Moon look alive. I didn’t want to publish this project because of its simplicity, and I thought it would not pass the review. But finally made a decision to try. In this article, I’ll share the key ideas and the code behind it. What happened eventually read below.

Rendering the Moon

The Moon is just a textured sphere. I found the 8K image here: https://commons.wikimedia.org/wiki/File:Solarsystemscope_texture_8k_moon.jpg

Using an 8K texture map makes the surface details sharp:

func setupMoon(for node: SCNNode, with radius: Double) {
    let sphere = SCNSphere(radius: radius)
    let material = SCNMaterial()
    material.diffuse.contents = UIImage(named: "moonAR.scnassets/8k_moon.jpg")
    sphere.materials = [material]
    node.position = SCNVector3(x: 0, y: 0, z: -3)
    node.geometry = sphere
}

Here, I reused the AR asset, simply adding this functionality mode because it has almost the same logic. I started by defining two rendering modes.

Switching Between 3D and AR

enum MoonMode {
    case main   // 3D Scene mode
    case ar     // AR mode
}
  • Main mode → shows the Moon in a regular SCNView.
  • AR mode → places the Moon into the real world using ARSCNView.

With a simple tap gesture, I can toggle between the two modes by animating the view's opacity.

For zooming, I attached a pinch gesture recognizer, allowing users to resize the Moon interactively.

Light and Shadows

Lighting is what makes the Moon feel realistic. I combined two types of lights:

  1. Ambient light → subtle base illumination.
  2. Directional light → acts like the Sun, creating realistic shadows and phases.
func setupDirectLight(for node: inout SCNNode) {
    node.light = SCNLight()
    node.light?.type = .directional
    node.light?.castsShadow = true
    node.light?.intensity = 2500
    node.light?.shadowSampleCount = 64
    node.light?.shadowRadius = 16
    node.position = SCNVector3(x: 0, y: 0, z: -3)
}

I also added an animation to change the light angle dynamically to simulate Moon phases:

func animatePhaseLightChange(with angle: Double) {
  let newVector = SCNVector3(
      0,
      -Float(angle - 14.5) * 0.21,
      0
  )

  let rotateAction = SCNAction.rotateTo(
      x: CGFloat(newVector.x),
      y: CGFloat(newVector.y),
      z: CGFloat(newVector.z),
      duration: 0
  )
  directLightNode.runAction(rotateAction)
  directARLightNode.runAction(rotateAction)
}

This gives the illusion of the Moon being lit differently depending on the phase.

AR Mode

In AR mode, I used ARWorldTrackingConfiguration to track the real environment. With default lighting enabled, the Moon blends nicely with the room’s light:

arMoonSceneView.autoenablesDefaultLighting = true

Switching between AR and Scene mode is just a matter of fading between SCNView and ARSCNView.

Result

The result is a smooth, interactive Moon that can be:

  • Zoomed in 3D and AR.
  • Dropped into the real world with AR, and you can go around to watch the full hidden surface.
  • Phase changed by swipes with animated different light angles.

And all of these work in offline mode with local calculations of phases without an external API.

Finally, I published the application. Apple asked me in detail during the review process: what is the purpose, who are my users, and what value does it bring? I described everything in detail, got approved and … Apple released the same logic in the Weather native iOS application 🤣 This shows high user demand, well, good. And yeah, I almost forgot, this application immediately rose to the 2nd place in the Weather category.

Next Steps

I integrated geomagnetic storms prediction already, and planning to extend this by improving shadows with environment maps. I found better data here: https://svs.gsfc.nasa.gov/4720.

I invite you to experiment with SceneKit or ARKit based on an existing open-sourced project, mixing 3D textures, lighting, and AR modes like this. It’s surprisingly fun to watch a realistic Moon floating in your living room! 🌙

Moonlight phase iOS application https://apps.apple.com/us/app/moonlight-phase/id1666801366

The full source code https://github.com/ndwdm/Moonlight-ios


메타데이터
post_id
9d3284eab8c6
slug
the-moons-other-side-3d-ar-how-i-created-the-top-app-store-ios-app-on-a-coffee-break-9d3284eab8c6
url
https://medium.com/antaeus-ar/the-moons-other-side-3d-ar-how-i-created-the-top-app-store-ios-app-on-a-coffee-break-9d3284eab8c6
canonical_url
https://medium.com/antaeus-ar/the-moons-other-side-3d-ar-how-i-created-the-top-app-store-ios-app-on-a-coffee-break-9d3284eab8c6
author_url
https://medium.com/@ndwdm
status
ok
fetched_at
2026-06-10 22:22:12