objective-cswiftshaderscenekitscnlight

Is there a way to make moving/changing shadows on Scenekit (like a rising sun)?


my basic code is pretty much this on that tutorial: https://code.tutsplus.com/tutorials/an-introduction-to-scenekit-fundamentals--cms-23847

I want to make a sunrise behaviour. Something like the lightNode begins on the same height as the constrained cubeNode and move it up so that the shadow shall become smaller over time. Therefore I tried to move the nodes via SCNAction.move(to...).

Action on lightNode -> nothing happens

Action on the constrained cubeNode -> shadow starts to flicker, but no changing

I tried around with the shadowModes. I think I misunderstood this. No useful results came up.

Has anyone an idea if scenekit supports something like dynamicly changing shadows?


Solution

  • I found a way to make it. My major mistakes were:

    1. Somehow it does not work to access the lightNode (it just has no effect, when actions for a lightNode are called)

    2. I tried to go via SCNAction.move(to...). The answer is to use a rotation instead of a longitudinal movement.

    The answer was accessing the constraint Node (instead of the lightNode). In this code the cubeNode is replaced with an invisible centerPoint (as the constraint node where the lightNode has to look at). A boxNodehas been added for making a canvas for the shadow. The lightNodehas to be added to the centerPoint, NOT to the scene.

    There is the modified viewDidLoad-method. If you want to check this out, open Xcode, start a new project as SceneKit Game and replace the viewDidLoad with the following code.

        override func viewDidLoad() {
        super.viewDidLoad()
    
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
    
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
    
        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
    
        // create and add a light to the scene
        let centerPoint = SCNNode.init()
        centerPoint.position = SCNVector3Make(0, 0, 0)
        scene.rootNode.addChildNode(centerPoint)
    
        let light = SCNLight()
        light.type = SCNLight.LightType.spot
        light.spotInnerAngle = 30
        light.spotOuterAngle = 80
        light.castsShadow = true
        light.color = UIColor.init(colorLiteralRed: 0.95, green: 0.8, blue: 0.8, alpha: 1)
        light.zFar = 200
    
        let lightNode = SCNNode()
        lightNode.light = light
        lightNode.position = SCNVector3Make(20, 100, 50)
    
        let constraint = SCNLookAtConstraint(target: centerPoint)
        constraint.isGimbalLockEnabled = true
        lightNode.constraints = [constraint]
        centerPoint.addChildNode(lightNode)
    
        let ambientLight = SCNLight.init()
        ambientLight.type = SCNLight.LightType.ambient
        ambientLight.color = UIColor.darkGray
    
        scene.rootNode.light = ambientLight
    
        // retrieve the ship node
        let material = SCNMaterial.init()
        material.diffuse.contents = UIColor.yellow
        material.lightingModel = SCNMaterial.LightingModel.phong
        material.locksAmbientWithDiffuse = true
    
        let boxNode = SCNNode.init(geometry: SCNBox.init(width: 10, height: 0.1, length: 10, chamferRadius: 0))
        boxNode.geometry?.firstMaterial = material
        boxNode.position = SCNVector3Make(0, -2, 0)
    
        scene.rootNode.addChildNode(boxNode)
    
        // animate spot light rotation
        centerPoint.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: CGFloat(M_PI), y: 0, z: CGFloat(M_PI), duration: 5)))
    
        // animate color light change
        let lightColorChange: CABasicAnimation = CABasicAnimation.init(keyPath: "color")
        lightColorChange.fromValue = UIColor.init(colorLiteralRed: 0.95, green: 0.8, blue: 0.8, alpha: 1)
        lightColorChange.toValue = UIColor.init(colorLiteralRed: 0, green: 0, blue: 0.4, alpha: 1)
        lightColorChange.duration = 5.0
        lightColorChange.autoreverses = true
        lightColorChange.repeatCount = Float.infinity
        light.addAnimation(lightColorChange, forKey: "changeLight")
    
        // retrieve the SCNView
        let scnView = self.view as! SCNView
    
        // set the scene to the view
        scnView.scene = scene
    
        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true
    
        // show statistics such as fps and timing information
        scnView.showsStatistics = true
    
        // configure the view
        scnView.backgroundColor = UIColor.black
    
        // add a tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)
    }
    

    There is also a color change of the light is considered. With a CABasicAnimation it is possible to change the light.color-property over time. It is not like a perfect sunrise with all color steps, but there is also a way to chain those animations to make it more complex. (For this search for "wenderlich how to create a complex loading animation".

    But I did not find a way to change the shadow color. It could be a nice effect to have white shadows on nighttime and black shadows on daytime. light.shadowColor did not help yet. If someone has an idea it is very appreciated.