Shadows & Lights in RealityKit
In AR mode, RealityKit will generate ground shadows and estimate lighting from the environment. It doesn't seem to cast any shadows on the model itself. If you create model entities in code, ground shadows don't seem to be generated.
You can download a demo app that demonstrates the above here.
Lights only seem to work in non-AR mode. You can set this mode by setting cameraMode
to .nonAR
when constructing an ARView
.
ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: true)
A RealityKit scene can contain up to eight dynamic lights which includes point, spot or directional lights. Only one directional light is supported in a scene. It can also contain an image light - you use a high dynamic range (HDR) image to generate the lighting. Lights are components but RealityKit provides wrapper entities for each of the light components.
let pointLight = PointLight() // pointLight is an entity
pointLight.light.intensity = 1000000 // pointLight.light is a component
pointLight.light.color = .blue
pointLight.light.attenuationRadius = 5
pointLight.position = [0, 0.2, 0]
anchorEntity.addChild(pointLight)
Point lights emit light in all directions. PointLight
class extends Entity
and has a PointLightComponent
assigned to the light
property. Point lights don't seem to cast any shadows.
- intensity: The intensity of the light measured in lumens.
- attenuationRadius: The distance in metres it takes for the light intensity to drop to zero i.e. the range of the light.
let spotLight = SpotLight()
spotLight.light.color = .green
spotLight.light.intensity = 1000000
spotLight.light.innerAngleInDegrees = 40
spotLight.light.outerAngleInDegrees = 60
spotLight.light.attenuationRadius = 10
spotLight.shadow = SpotLightComponent.Shadow()
spotLight.look(at: [1, 0, -4], from: [0, 5, -5], relativeTo: nil)
anchorEntity.addChild(spotLight)
Spot lights are like desk lamps - they emit light in a cone shape. If you want the spot light to cast shadows you will need to set the shadow
property to a new SpotLightComponent.Shadow
instance.
- innerAngleInDegrees: The angle in degrees where the light begins to fade.
- outerAngleInDegrees: The angle in degrees where the fading light ends.
let directionalLight = DirectionalLight()
directionalLight.light.color = .red
directionalLight.light.intensity = 1000000
directionalLight.shadow?.maximumDistance = 5
directionalLight.shadow?.depthBias = 1
directionalLight.look(at: [1, 0, 1], from: [0, 1, 0], relativeTo: nil)
anchorEntity.addChild(directionalLight)
A directional light is like the sun - it lights everywhere with the same intensity. It doesn't have a position but it does have a direction. DirectionalLight
automatically assigns a DirectionalLightComponent.Shadow
to a shadow
property with maximumDistance
set to 5 and depthBias
set to 1.
- shadow?.maximumDistance: If the distance between the object that is casting shadows and the object that is receiving shadows is equal or less than this value, shadows will be displayed.
- shadow?.depthBias: Changing this value didn't seem to have any visible difference.
The shadows for lights are razor sharp and there doesn't seem to be a way to adjust them.
Image Light
RealityKit supports a high dynamic range image as a light. RealityKit supports these images either in HDR or OpenEXR format. Polyhaven is a good place to get free HDRIs - it supports both formats. Copy the image into the left sidebar of XCode making sure that the target is checked next to "Add to targets".
arView.environment.lighting.resource = try! .load(named: "dreifaltigkeitsberg_2k.hdr")
arView.environment.lighting.intensityExponent = 1
You don't specify the intensity directly instead you specify an exponent e.g. an exponent of 2 is 10^2 = 100, an exponent of 0 is 10^0 = 1 and an exponent of -2 is 10^-2 = 0.01.
You can download an app that demonstrates adding lights here.