Search code examples
iosswiftscenekitarkitarscnview

ARKit + SceneKit not rendering any shadows


I'm using ARKit and SceneKit to render a very simple scene with a sphere hovering above a plane. However no matter what I try, I cannot get shadows to render at all. The sphere is shaded properly from the light, but no shadows are drawn.

Here's my complete ARSCNView:

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    public var baseNode = SCNNode()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView.delegate = self
        sceneView.autoenablesDefaultLighting = false
        sceneView.automaticallyUpdatesLighting = false
        sceneView.rendersCameraGrain = true
        sceneView.preferredFramesPerSecond = 0
        sceneView.debugOptions = [.showBoundingBoxes]
        
        let scene = SCNScene()
        sceneView.scene = scene
        
        self.baseNode = SCNNode()
        baseNode.position.z -= 1 // draw in front of viewer at arts
        self.sceneView.scene.rootNode.addChildNode(baseNode)

        // Plane to catch shadows
        let shadowCatcher = SCNNode(geometry: SCNPlane(width: 0.5, height: 0.5))
        shadowCatcher.name = "shadow catcher"
        shadowCatcher.castsShadow = false
        shadowCatcher.renderingOrder = -10

        let groundMaterial = SCNMaterial()
        groundMaterial.lightingModel = .constant
        groundMaterial.isDoubleSided = true
        shadowCatcher.geometry!.materials = [groundMaterial]
        self.baseNode.addChildNode(shadowCatcher)
        
        // A shere that should cast shadows
        let sphere = SCNNode(geometry: SCNSphere(radius: 0.05))
        sphere.position = SCNVector3(0, 0, 0.3)
        sphere.castsShadow = true
        baseNode.addChildNode(sphere)
                
        // The light
        let light = SCNLight()
        light.type = .spot
        light.intensity = 1000
        light.castsShadow = true
        light.shadowMode = .deferred
        light.automaticallyAdjustsShadowProjection = true
        light.shadowMapSize = CGSize(width: 2048, height: 2048)

        let lightNode = SCNNode()
        lightNode.name = "light"
        lightNode.light = light
        lightNode.position = SCNVector3(0, 0, 2)
        lightNode.look(at: SCNVector3(0, 0, 0))
        baseNode.addChildNode(lightNode)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        let configuration = ARWorldTrackingConfiguration()
        
        if ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentation) {
            configuration.frameSemantics.insert(.personSegmentation)
        }

        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }
}

Why doesn't this render shadows? The same basic scenegraph does render shadows if I use a normal SCNView instead of an ARSCNView


Solution

  • This is caused by enabling the personSegmentation frame semantic. After removing this, shadows should be rendered properly again:

    A shadow being cast

    This took me forever to track down and seems like a bug. I've filed an issue against Apple but unfortunately I am not aware of any workarounds at the moment