3

I have an AR app in which I have calculated current sun position from the user. I reduced this position to distance of 10,000 meters, so it kinda "stays" in place when I move inside the scene. Now I would like to cast shadows from this node on other nodes inside my scene. I tried few types of lightning, but none was successful. Some didn't drop shadow at all, others behave strange. What would be the best method to create a light source from such distant node to cast shadows on invisible floor node? Should I also add ambient light to the scene? Can it be added to camera node, or should be somewhere else?

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Damian Dudycz
  • 2,622
  • 19
  • 38

1 Answers1

2

Use directional light for Sun simulation (Sun has parallel rays that for us are primary rays from very distant light source) and use ambient light for simulating fake secondary rays (in case you do not use Global Illumination).

// DIRECTIONAL LIGHT for `primary light rays` simulation
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .directional
lightNode.light!.castsShadow = true
lightNode.light!.shadowMode = .deferred
lightNode.light!.categoryBitMask = -1
lightNode.light!.automaticallyAdjustsShadowProjection = true
//lightNode.light!.maximumShadowDistance = 11000
lightNode.position = SCNVector3(x: 0, y: -5000, z: 0)
lightNode.rotation = SCNVector4(x: -1, y: 0, z: 0, w: .pi/2)
scene.rootNode.addChildNode(lightNode)

Tip: The position of directional light could be any (in the following code it is even under the plane y:-5000), the most important thing is direction of directional light!

Also directional light has no falloff parameter or, so called, quadratic light decay.

// AMBIENT LIGHT for `fake secondary light rays` simulation
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.intensity = 1000
ambientLightNode.light!.color = NSColor.white
scene.rootNode.addChildNode(ambientLightNode)

Do not mistakenly use this Boolean value:

lightNode.castsShadow = true

instead of this one:

lightNode.light!.castsShadow = true

The Boolean value lightNode.castsShadow determines whether SceneKit renders the node’s contents into shadow maps.

And here's a screenshot if you wish to enable shadows in manually created directional light:

enter image description here

Sometimes, you get some benefits if you attach a light to the camera. In that case object's surfaces with normals parallel to light's rays are lit.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • Thanks, I'll try later. I think I was experimenting with directional light, and the problem was that it was casting shadows only from close distances, and it would be easier for me if it could be at distance of 100_000 meters. Anyway if it will be necessary, I'll reduce this distance, and keep updating it when camera moves. – Damian Dudycz Oct 04 '18 at 15:26
  • This works, but I have one more issue. There are no shadows when I use SceneKit, but it works fine in ARKit. Can you think of a reason why? This is how I create floor: let floor = SCNFloor() floor.firstMaterial!.colorBufferWriteMask = [] floor.firstMaterial!.readsFromDepthBuffer = true floor.firstMaterial!.writesToDepthBuffer = true floor.firstMaterial!.lightingModel = .constant – Damian Dudycz Oct 04 '18 at 20:02
  • Please, publish it as another question. I'll answer it. Let me know when you publish it. – Andy Jazz Oct 04 '18 at 20:14
  • I have posted new question: https://stackoverflow.com/questions/52654624/scenekit-shadow-in-transparent-floor – Damian Dudycz Oct 04 '18 at 20:24