Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2026 Graphics & GamesSpatial Computing

WWDC26 · 24 min · Graphics & Games / Spatial Computing

Explore advances in RealityKit

Discover the latest advancements in RealityKit designed to make your apps and games more immersive and realistic than ever. Explore powerful new capabilities including interactive cloth simulations, NavMesh pathfinding, mixed reality lighting, and customizable reverb meshes for enhanced spatial audio. Elevate your visual fidelity with improved shadows, character rendering enhancements, and support for Gaussian splatting.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 11 snippets

Soft shadows swift · at 4:02 ↗
// Enable soft shadows for the hearth spotlight
    
guard var shadow = hearthSpotlight.components[SpotLightComponent.Shadow.self] else {
    // handle error
}
shadow.lightSize = 0.7 // meters
    
shadow.quality = .medium // or .high
// shadow.quality = .low // will result in hard shadows
    
hearthSpotlight.components.set(shadow)
Projective textures swift · at 6:13 ↗
// Create one of the planetarium spotlights
    
let spotLightEntity = Entity()
spotLightEntity.components.set(SpotLightComponent(
    color: .white,
    intensity: intensity,
    innerAngleInDegrees: innerAngle,
    outerAngleInDegrees: outerAngle,
    attenuationRadius: attenuationRadius,
))
    
let projectiveTexture: TextureResource = generateStarsAndNebulaeTexture()
spotLightEntity.components.set(SpotLightComponent.ProjectiveTexture(
    texture: projectiveTexture
))
Physical space lighting swift · at 7:13 ↗
// Enable physical space lighting
    
spotLightEntity.components.set(SpotLightComponent.SurroundingsLight())
Querying the navigation mesh swift · at 9:46 ↗
// Querying the navigation mesh in Chaparral Village

extension Entity {
    public func navigate(/* ... */) async {
        let navigator = try! NavigationController(entity: self)
        guard let result = await navigator.computePath(from: fromPosition, to: toPosition) 
        else {
            return
        }
        if result.isEmpty {
            return
        }
        for node in result {
            switch node.category {
                case .meshPoint:
                    finalPath.append(node.position)
                case .offMeshConnection:
                    // handle ladders
            }
        }
    }
}
Pinning cloth to anchor points swift · at 12:51 ↗
// Pin the curtains to the Alchemist's lab

for (pin, pinComponent) in pins {
        let position = pin.position(relativeTo: event.entity)
        let selectionSphere = ClothSphereShape(radius: pinComponent.radius)
    
        let vertices = clothMesh.vertices(in: .sphere(selectionSphere),
                                    center: position)
        clothBody.motionTypes.set(vertexIndices: vertices, value: .kinematic)
}
LOD by camera distance swift · at 14:42 ↗
// Create entity with LODs
    
let lod0 = [ModelEntity(mesh: lodMesh0)]
let lod1 = [ModelEntity(mesh: lodMesh1)]
let lod2 = [ModelEntity(mesh: lodMesh2)]

let entity = Entity()

LevelOfDetailComponent.addByCameraDistance(to: entity, levels: [
    (entities: lod0, maxDistance: 1.0 /* meters */), // highest detail
    (entities: lod1, maxDistance: 5.0),              // medium detail
    (entities: lod2, maxDistance: .infinity),        // lowest detail
])
LOD by screen area swift · at 15:58 ↗
// Create entity with LODs
    
let lod0 = [ModelEntity(mesh: lodMesh0)]
let lod1 = [ModelEntity(mesh: lodMesh1)]
let lod2 = [ModelEntity(mesh: lodMesh2)]

let entity = Entity()

LevelOfDetailComponent.addByScreenArea(to: entity, levels: [
    (entities: lod0, minArea: 0.2 /* fraction of screen area */),  // highest detail
    (entities: lod1, minArea: 0.1),                                // medium detail
    (entities: lod2, minArea: 0.01),                               // lowest detail
])
Responding to thermal state changes swift · at 16:26 ↗
// Respond to changes in device thermal state
    
NotificationCenter.default.addObserver(of: ProcessInfo.self,
                                       for: .thermalStateDidChange) {_ in
    switch ProcessInfo.processInfo.thermalState {
        case .nominal, .fair:
            // Stay the course
        case .serious, .critical:
            // Improve performance by:
            // More aggressive LOD switching
            // Lower shadow quality
    }
}
Creating a Gaussian splat swift · at 18:44 ↗
// Create Gaussian splat resource and component

let resource = try GaussianSplatResource.BufferResource(count: splatCount,
                                                        position: positionBuffer,
                                                        scale: scaleBuffer,
                                                        rotation: rotationBuffer,
                                                        opacity: opacityBuffer,
                                                        sphericalHarmonics:
                                                                                                                    (sphericalHarmonicsBuffer, degree))

let splatResource = GaussianSplatResource(resource)

let splatComponent = GaussianSplatComponent(splatResource)

splatEntity.components.set(splatComponent)
Creating a custom reverb mesh swift · at 20:49 ↗
// Create and use custom reverb mesh

let mesh: ReverbMeshResource = .shoebox(size: [5, 4, 6])
    
let reverb: Reverb = .simulated(mesh: mesh, materials: [.dryWall])
    
entity.components.set(ReverbComponent(reverb: reverb))
Creating custom reverb materials swift · at 21:33 ↗
// Create custom materials for custom reverb mesh
    
let thickCarpet: Audio.Material = .carpet.scalingAbsorption {freq in 0.1 }
    
let bookshelf: Audio.Material
    
// Absorption coefficients by center frequency:
// 31.5Hz, 63Hz, 125Hz, 250Hz, 500Hz, 1kHz, 2kHz, 4kHz, 8kHz, 16kHz
let bookshelfAbsorption = Audio.Absorption(
    [0.10, 0.15, 0.28, 0.20, 0.15, 0.10, 0.10, 0.07, 0.07, 0.05])
    
// Scattering coefficients for: 500Hz, 1000Hz, 4000Hz
let bookshelfScattering = Audio.Scattering([500: 0.5, 1000: 0.6, 4000: 0.7])
    
bookshelf = .init(absorption: bookshelfAbsorption,
              scattering: bookshelfScattering)

Resources