swift – High-Quality Rendering – RealityKit vs SceneKit vs Metal

Updated: May 23, 2022.

TL;DR


RealityKit is the youngest SDK in Apple family of rendering technologies. This high-level framework was released in 2019. RealityKit is made for AR/VR projects, has simplified settings for multi-user experience and can be used on iOS/macOS. Performs multithreaded rendering.

There’s no Objective-C legacy, RealityKit supports only Swift, and rather declarative syntax (like in SwiftUI). The main advantage of RealityKit – it can complement / change / customise scenes coming from Reality Composer app and can be a powerful extension for ARKit – although it shines as a standalone AR SDK as well. In RealityKit the main units are companies (ModelEntity, AnchorEntity, TriggerVolume, BodyTrackedEntity, PointLight, SpotLight, DirectionalLight and PerspectiveCamera) that has components and can be created from resources like ModelEntity. The framework runs an Entity Component System (ECS) on the CPU to manage tasks like physics, animations, audio processing, and network synchronization. But it relies on the Metal and GPU hardware to perform multithreaded rendering. RealityKit has six materials: UnlitMaterial, SimpleMaterial, PhysicallyBasedMaterial (with 18 AOVs for controlling material’s look), OcclusionMaterial, VideoMaterial and, of course, CustomMaterial.

enter image description here

Pay particular attention to shadows on iOS – devices up to A11 chipset produce projective (aka depth map) shadows, but on devices with A12 and higher we can see raytraced shadows. Maybe your solution in this case could be fake shadows. Many contemporary render features in RealityKit are On by default: camera’s depth of field, face/person occlusion, grounding shadows, motion blur, camera grain, etc.

Sample code in Swift:

@IBOutlet weak var arView: ARView!

let box = MeshResource.generateBox(size: 0.5)
var material = PhysicallyBasedMaterial()
let model = ModelEntity(mesh: box, materials: [material])
    
let anchor = AnchorEntity(world: [0, 0,-1])
anchor.addChild(model)

arView.scene.anchors.append(anchor)

RealityKit reads in .usdz, .rcproject and .reality file formats. Supports transform and asset animation, rigid body dynamics, PBR materials, HDR Image Based Lighting, raycasting and spatial audio. All scene models must be tethered with anchors (AnchorEntity class). Framework automatically generates and uses mipmaps, which are a series of progressively low-rez variants of objects’ texture that improve render times when applied to distant objects. RealityKit works with a polygonal mesh generated using Scene Reconstruction feature. I wanna add a few words about AR Quick Look – a zero-settings framework that’s built on the RealityKit engine and it’s conceived for fast AR visualization.

Conclusion: RealityKit gives you a high-quality render technology and up-to-date AR capabilities out-of-the-box. Supports LiDAR Scanner. Supports Photogrammetry tools. Plays Reality Composer’s behaviors through its notification API. RealityKit can be used as a standalone framework, or as a partner of ARKit and MetalKit. Starting from iOS 15 we have access to fragment/pixel shaders and geometry modifiers via Metal scripting and CustomMaterials. Reality family software has CLI and GUI tools for fast and easy USDZ-conversion.

RealityKit works with UIKit storyboards or with SwiftUI interfaces. It has a minimum of a boilerplate code. For example, RealityKit has a very simple setup for models’ collision and for gestures (pan, rotate, pinch), including alternative 2D gestures. And there’s composition over inheritance, so it’s rather a Protocol Oriented Programming framework – tight coupling in most cases is no longer a problem in your code. RealityKit fits perfectly with the Combine reactive paradigm, which helps handling publishers, subscribers and asynchronous events. A prime example of it, is the subscribe(to:on:_:) generic instance method that returns an object representing the subscription to an event stream, like SceneEvents.Update.self which triggered once per frame interval (60 fps).

RealityKit’s native view is ARView.

@available(OSX 10.15, iOS 13.0, *)
@objc open class ARView : ARViewBase

enter image description here

SceneKit is a high-level framework as well. The oldest one in Apple family of rendering technologies. It was released in 2012. SceneKit was conceived for VR and can be run on iOS/macOS. For AR projects you can use it only in conjunction with ARKit. SceneKit supports both Objective-C and Swift. In SceneKit the main unit is a node (SCNNode class) that has its own hierarchy and can store a light (SCNLight), or a camera (SCNCamera), or a geometry (SCNGeometry), or a particle system (SCNParticleSystem), or audio players (SCNAudioPlayer). The main advantage of SceneKit – it’s highly customisable, it can change geometry and materials at runtime, it has morphers, skinners and constraints, it renders a scene up to 120 fps and it has an advanced setup for a particle system. There are Blinn, Constant, Lambert, Phong, ShadowOnly and PBR shaders.

Occlusion The shader is also available for us in SceneKit but in a custom form (there’s no out-of-the-box occlusion material here like we can find in RealityKit). In case you need a video material in SCNScene you should implement SpriteKit’s SKVideoNode.

enter image description here

SceneKit allows you to render such effects as beautiful deep depth of field, vignette effect, screen space reflections, or even CoreImage filters with animated parameters. Also we can use an SCNProgram object to perform custom rendering. It’s a complete Metal or OpenGL shader program that replaces SceneKit’s rendering of a material or even geometry. SceneKit’s reliable companion is a Model I/O library that carries out import, export, and models’ manipulation using a common infrastructure.

Sample code in Objective-C:

SCNView *sceneView = (SCNView *)self.view;
SCNScene *scene = [SCNScene scene];
    
sceneView.scene = scene;
sceneView.autoenablesDefaultLighting = YES;
sceneView.allowsCameraControl = YES;
    
SCNNode *box = [SCNNode node];
box.geometry = [SCNBox boxWithWidth:0.5 height:0.5 length:0.5 chamferRadius:0];
box.geometry.firstMaterial.lightingModelName = SCNLightingModelPhysicallyBased;
box.geometry.materials[0].diffuse.contents = [UIColor systemRedColor];
box.geometry.materials.firstObject.metalness.contents = @1.0;
[scene.rootNode addChildNode:box];

SceneKit reads in .usdz, .dae and .scn file formats. Supports nested asset animation, dynamics, particles, PBR materials, HDR IBL and spatial audio. For implicit and explicit transform animation of any node you can use SCNAction, SCNTransaction and CAAnimation classes. Though a collisions’ setup in SceneKit is a little bit complicated. To create a modular and scalable game architecture with SceneKit we need to implement GameplayKit’s entity-component pattern.

Conclusion: SceneKit gives you a high-quality render technology (but at first you need to setup physicallyBased shaders), although for AR projects you can use it only with ARKit. SceneKit is highly customisable and can be used with Swift and Objective-C, and it gives you a set of useful renderer(...) instance methods coming from ARSCNViewDelegate protocol that allows you to update AR models and tracked anchors at 60 fps. Works with UIKit and SwiftUI (despite the fact that there is no SceneKit+SwiftUI template in Xcode). There are obvious reasons that Apple might make this framework deprecated during the next 3 years – SceneKit hasn’t been updated since 2017 (excluding minor changes, like clearCoat material property, or SSR). But SceneKit still has several advantages over RealityKit 2.0. One thing Swift developers forget about is that Objective-C SceneKit’s apps ensure fast compile times.

SceneKit’s native view is SCNView.

@available(iOS 8.0, tvOS 9.0, *)
open class SCNView : UIView, SCNSceneRenderer, SCNTechniqueSupport 
 
@available(OSX 10.8, *)
open class SCNView : NSView, SCNSceneRenderer, SCNTechniqueSupport 

enter image description here

To be precise, Metal is not a rendering technology but rather the GPU accelerator with the ability of using a rich shading language (MSL). It was released in 2014. It’s a low-level framework. Metal is implemented everywhere – in RealityKit, SceneKit, ARKit, CoreML, Vision, AVFoundation, etc. Metal combines functions similar to OpenGL and OpenCL under the hood of just one API. Of course, Metal can be used as a renderer for advanced 3D graphics. Metal renders not only reflections but also refractions and subsurface scattering phenomena.

According to Apple documentation: Metal is a C++ based programming language that developers can use to write code that is executed on the GPU for graphics and general-purpose data-parallel computations. Since Metal is based on C++, developers will find it familiar and easy to use. Metal, both graphics and compute programs can be written with a single, unified language, which allows tighter integration between the two.

In addition to Metal, you can use MetalKit module (released in 2015) that helps build Metal apps quicker and easier, using far less code. It renders graphics in a standard Metal view, load textures from many sources, and work efficiently with models provided by Model I/O framework.

Metal begins shining when you render a greater number of polygons or 3D-particles than SceneKit or RealityKit is capable of rendering.

You will be surprised when you know that Metal can be used not only in the Apple ecosystem but also in Windows. Here’s a link where you can download Metal Developer Tools for Windows.

Sample code:


import MetalKit
    
class RedCube: Primitive {
        
    override func buildVertices() {
            
        vrtx = [ Vertex(position: float3(-1, 1, 1), color: float4(1,0,0,1)),
                 Vertex(position: float3(-1,-1, 1), color: float4(1,0,0,1)),
                 Vertex(position: float3( 1, 1, 1), color: float4(1,0,0,1)),
                 Vertex(position: float3( 1,-1, 1), color: float4(1,0,0,1)),
                 Vertex(position: float3(-1, 1,-1), color: float4(1,0,0,1)),
                 Vertex(position: float3( 1, 1,-1), color: float4(1,0,0,1)),
                 Vertex(position: float3(-1,-1,-1), color: float4(1,0,0,1)),
                 Vertex(position: float3( 1,-1,-1), color: float4(1,0,0,1)) ]
    
        indices = [ 0,1,2, 2,1,3, 5,2,3, 
                    5,3,7, 0,2,4, 2,5,4,
                    0,1,4, 4,1,6, 5,4,6, 
                    5,6,7, 3,1,6, 3,6,7 ]
    }
}


class CubeScene: Scene {

    override init(device: MTLDevice) {           
        super.init(device: device)
            
        let redCube = RedCube(withDevice: device)
        objects.append(redCube)
        redCube.translate(direction: float3(0,0,-10))
        add(child: redCube)
    }
        
    override func render(commandEncoder: MTLRenderCommandEncoder, 
                              deltaTime: Float) {

        objects.forEach { $0.rotate(angle: deltaTime, 
                                     axis: float3(1, 1,-1)) }

        super.render(commandEncoder: commandEncoder, 
                          deltaTime: deltaTime)
    }
}

Conclusion: Developers usually use Metal framework to generate a High-Quality GPU Rendering for games with sophisticated 3D environments, for video processing apps like Final Cut Pro and Nuke, for 3D apps like Maya, or for big data scientific apps that must perform for scientific research . Consider, raytracing in Metal is much more quicker than in RealityKit.

MetalKit’s native view is MTKView.

@available(iOS 9.0, tvOS 9.0, *)
open class MTKView : UIView, NSCoding, CALayerDelegate

@available(OSX 10.11, *)
open class MTKView : NSView, NSCoding, CALayerDelegate

enter image description here

SpriteKit is an Apple node-based framework for creating and rendering 2D games and 2D graphics. Was released in 2013. You can use SpriteKit as a standalone API or use it with SceneKit and ARKit. Its main feature is the ability to draw sprites with physics, 2D text and shapes, images and video, and even to rasterize SceneKit’s scenes. In SpriteKit you can write a code in Objective-C or Swift.

Official documentation: “SpriteKit is a general-purpose 2D framework that leverages Metal to achieve high-performance rendering, while offering a simple programming interface to make it easy to create games and other graphics-intensive apps. Using a rich set of animations and physics behaviours, you can quickly add life to your visual elements and gracefully transition between screens”.

SpriteKit works with two native types of view that inherit from UIView and NSView:

@available(iOS 7.0, tvOS 9.0, *)
open class SKView : UIView

@available(OSX 10.9, *)
open class SKView : NSView

enter image description here

Speaking of RealityKit, SceneKit or SpriteKit, it’s impossible not to mention the ARKit framework. RealityKit, like ARKit, is based on ARSession and ARConfiguration objects, thus, if ARKit is being updated, then RealityKit is being updated too. ARKit is the umbrella that includes SceneKit, SpriteKit, AVFoundation, Vision, UIKit, CoreMotion and CoreGraphics dependencies. ARKit is served in both languages ​​- Swift and Objective-C.

ARKit has no any rendering engine inside. This module is only responsible for high-quality Camera/Object Tracking and Scene Understanding (plane detection, ray-casting, scene reconstruction and light estimation). However, ARKit is capable of processing data of a Canonical Facial Meshes (ARFaceGeometry class) or Vertices, Normals, Triangular Faces, and Classifications of a reconstructed geometry (ARMeshGeometry class).

Here are four types of views ARKit is able to work with: ARSCNView, ARSKView, ARView and MTKView.

@available(iOS 11.0, *)
open class ARSCNView : SCNView, ARSessionProviding

@available(iOS 11.0, *)
open class ARSKView : SKView, ARSessionProviding

@available(iOS 13.0, *)
@objc open class ARView : ARViewBase

@available(iOS 9.0, *)
open class MTKView : UIView, NSCoding, CALayerDelegate

If you need an additional information on ARKit and its capabilities, please read this POST.

Leave a Comment