skip to Main Content

I have a problem with shadows in RealityKit and DirectionalLight.

After doing some research from my initial question about this, I downloaded an Xcode project that describes the basics of lights in RealityKit. It’s simple enough.

One of the examples used in that sample project involved a DirectionalLight, which can be very helpful for lighting like the sun.

I took a look at the DirectionalLight option in the app, and added an extra sphere the scene:

A screenshot of an Xcode iPhone 15 Pro with correct directional lighting.

Here’s the lighting for that:

private func addDirectionalLight() {
        let directionalLight = DirectionalLight()
        directionalLight.light.intensity = 100000
        directionalLight.shadow?.maximumDistance = 5
        directionalLight.shadow?.depthBias = 1
        directionalLight.look(at: [0, 0, 0], from: [-50, 20, 0], relativeTo: nil)
        
        addLight(directionalLight)
    }

I made the intensity of it pretty high so you can easily see the light (white part) vs the shadows (non-white part).

I then went to my own project.

I want to create a directional lighting setup to achieve an effect akin to the sun. I thought that would be easy… I would just have to look at how the sample project did it and make adjustments.

Well… I tried creating my own lighting setup as follows:

let directionalLight = DirectionalLight()
        directionalLight.light.intensity = 100000
        directionalLight.shadow?.maximumDistance = 3
        directionalLight.look(at: [0, 0, 0], from: [0, 100, 1200], relativeTo: nil)
        // directionalLight.orientation = simd_quatf(angle: .pi/2, axis: [0, 1, 0])
        
        lightAnchor.addChild(directionalLight)

where lightAnchor is an AnchorEntity at [0, 0, 0] for the world.

Here’s the result when applied to objects:

A screenshot of an Xcode iPad Pro simulator with incorrect directional lighting.

Do you notice anything? I circled it. It’s like the lighting is being applied to each individual object without any sort of regard for the fact that another object is there.

In real life, a shadow would be casted from the sphere on top of the cube in addition to the shadow that is currently present. Look at the first image and pretend it is the top of the cube from the second photo. Yes, there’s a shadow on the back of the sphere where the light doesn’t hit, but ALSO on the plane it’s on.

I am extremely confused.

The only thing I thought of is that the first image has a scale where the sphere in the center of the photo is about 3 meters wide… while my second image is at a much, much larger scale. That box is 150 meters wide.

But, that should not impact the lighting, right? I don’t believe it should. Is my DirectionalLight in a bad position? I don’t understand how it would be, considering that it hit box and sphere.

I simply want shadows like in the first image, but for some reason I cannot achieve that.

I really need some help on this. It would be amazing if someone could!

Also, happy Vision Pro day! 🙂

EDIT 1/2:

I changed to my actual model (which is just one, big USDZ — the first photos were just using example meshes) and lighting code to this:

let directionalLight = DirectionalLight()
        directionalLight.light.intensity = 10000
        directionalLight.shadow?.depthBias = 0.5
        directionalLight.shadow?.maximumDistance = 3
        directionalLight.orientation = simd_quatf(angle: -.pi/5, axis: [1, 0, 0])
        
        lightAnchor.addChild(directionalLight)

This results in shadows looking like this:

A screenshot of an Xcode iPad Pro simulator with incorrect directional lighting.

It looks kind of weird. I have tried looking at the documentation, but I still don’t understand why I am not getting this right.

EDIT 2/2:

I have discovered another interesting property about shadows in relation to the PerspectiveCamera.

After thinking that the large dimensions of my model had an impact on the absence of shadows, I scaled down the model by a considerable amount.

This did actually partially solve the issue. I could now observe shadows on parts of my model:

A screenshot of an Xcode iPad Pro simulator with shadows and a closer camera position.

I then moved the PerspectiveCamera that I created back from the model a bit:

A screenshot of an Xcode iPad Pro simulator with shadows and a more distant camera position.

You have to look carefully here. Compare the two photos. I circled one particular part of the model where the change is very clear. The shadows moved, and even disappeared.

This also happens when I change the fieldOfViewInDegrees on the PerspectiveCamera.

This is very odd behavior. Why would the camera impact the shadows at all? Rendering settings? It’s like I need to ignore the rendering settings and have them not change based on where the camera is/FOV/zoom level etc. Any solutions?

I really have no idea what to do and I’m worried. Especially since I want to animate the camera (or model) moving and zooming in/out. I also want to animate the position of the DirectionalLight.

Perhaps I need a more detailed explanation of depthBias and maximumDistance and how they relate to the PerspectiveCamera, its FOV and rendering.

2

Answers


  1. Shadows in iOS RealityKit

    Use this code to setup shadows. The quality of shadows in RealityKit for iOS does not stand up to criticism. These shadows are difficult to compare to shadows in SceneKit, much less to shadows in 3D authoring tools such as Maya or Cinema4D. And unfortunately, RealityKit provides too few options for customizing shadows.

    import SwiftUI
    import RealityKit
    
    struct ContentView : View {
        var body: some View {
            ARViewContainer().ignoresSafeArea()
        }
    }
    

    enter image description here

    struct ARViewContainer : UIViewRepresentable {
    
        let arView = ARView(frame: .zero)
        let cube = ModelEntity(mesh: .generateBox(size: 1))
        let ball = ModelEntity(mesh: .generateSphere(radius: 0.3))
        let mat = SimpleMaterial(color: .gray, roughness: 1, isMetallic: false)
        
        init() {
            cube.model?.materials = [mat]
            ball.model?.materials = [mat]
        }        
        func makeUIView(context: Context) -> ARView {
            arView.cameraMode = .nonAR
            ball.position.y = 0.55
            
            let sun = DirectionalLight()
            sun.orientation = simd_quatf(angle: -.pi/4, axis: [1, 1.2, 0])
            sun.light.intensity = 12_000
            sun.shadow = DirectionalLightComponent.Shadow()
            sun.shadow?.maximumDistance = 3
            sun.shadow?.depthBias = 0.5
                    
            let anchor = AnchorEntity()
            anchor.orientation = simd_quatf(angle: .pi/3, axis: [1, 0, 0])
            anchor.addChild(sun)
            anchor.addChild(cube)
            anchor.addChild(ball)
            arView.scene.anchors.append(anchor)
            return arView
        }
        func updateUIView(_ view: ARView, context: Context) { }
    }
    
    Login or Signup to reply.
  2. From your explanations and observations of how the shadow behaves with large meshes and movement of PerspectiveCamera, I believe your issue is with the ‘maximumDistance’ property of DirectionalLight.Shadow.

    This property tells RealityKit how far from the viewpoint (PerspectiveCamera) it will consider meshes for shadow-casting calculations. So in your case with very large meshes and maximumDistance = 3, the camera is simply too far away from the object for RealityKit to even consider calculating its casted shadow. This would also happen when you move the camera too far away from your USDZ model as in your second example.

    Try playing with the maximumDistance property to find a value that better captures the use-case and scale of your RealityKit scene. For example:

    var directional = DirectionalLight()
    directional.shadow = .init(maximumDistance: 128, depthBias: 1.0)
    

    Keep in mind that the ‘sharpness’ and quality of cast shadows decrease as the maximum distance is increased, so I suggest finding a value as close to the estimated maximum distance for your use case as possible.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search