Add shadow to SCNPlane

Viewed 325

I have ARKit artwork app. And I want my artworks to have a shadow just behind it. The shadow mustn't be dynamic. Just static shadow.

I've tried to add a plane behind the artwork. And set a shadow to it. But I discovered that it is not easy.

Can you help me?

let shadow = SCNPlane(width: artwork.size.width * 0.0254 + 0.03, height: artwork.size.height * 0.0254 + 0.015)

let layer = CALayer()
layer.frame = CGRect(origin: .zero, size: CGSize(width: shadow.width, height: shadow.height))
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 1
layer.shadowOffset = .zero
layer.shadowRadius = 10

shadow.firstMaterial?.diffuse.contents = layer

The shadow should be like in UIKit's UIView. But this code just doesn't work.

I need something like this

1 Answers

UPDATED.

In order to cast and receive a shadows from 3D lights, you have to use a 3D primitives (like cube, sphere, cylinder, etc), or 3D models in such formats as .usdz, .dae or .obj.

Shadow on a visible plane.

Use the following code to test how light generates shadows for 3D geometry:

let scene = SCNScene()

let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .spot
lightNode.light!.castsShadow = true
lightNode.light!.shadowMode = .deferred
lightNode.rotation = SCNVector4(x: -1, y: 0, z: 0, w: CGFloat.pi/2)
lightNode.position = SCNVector3(x: 0, y: 20, z: 0)
scene.rootNode.addChildNode(lightNode)

let sphereNode = SCNNode()
sphereNode.geometry = SCNSphere(radius: 2)
sphereNode.position = SCNVector3(x: 0, y: -2, z: 0)
scene.rootNode.addChildNode(sphereNode)

let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 15, height: 15)
planeNode.position = SCNVector3(x: 0, y: -5, z: 0)
planeNode.rotation = SCNVector4(x: -1, y: 0, z: 0, w: CGFloat.pi/2) 

planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red

scene.rootNode.addChildNode(planeNode)

Shadow on invisible plane.

In case you need an invisible plane that receives a shadow, use the following code:

planeNode.geometry?.materials.first?.colorBufferWriteMask = []
planeNode.geometry?.materials.first?.writesToDepthBuffer = true
planeNode.geometry?.materials.first?.lightingModel = .constant

Fake shadow.

And if you want to use a fake shadow written as a .png (png file format can hold 4 channels) texture on geometry (with a premultiplied alpha channel – RGB x A), use the following approach:

planeNode.geometry?.materials.first?.diffuse.contents = UIImage(named: "shadow.png")

lightNode.light!.castsShadow = false

Here's my answer on fake shadows.

And here's your premultiplied shadow in .png format (just drag-and-drop it on your desktop):

enter image description here

You can change its size and transparency and, of course, you can blur it.

Related