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

702 Views Asked by At

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?

2

There are 2 best solutions below

0
On BEST ANSWER

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.

3
On

Yes. You can make a beautiful moving shadow. If your spotlight isn't moving it may be because the tutorial had you put a constraint on it. Try removing it.

// lightNode.constraints = [constraint]