Implementing special rendering effects with RealityKit postprocessing
At a glance
| Item | Summary |
|---|---|
| Purpose | Implement a variety of postprocessing techniques to alter RealityKit rendering. |
| App architecture | AppDelegate hosts a SwiftUI-backed RealityKit view; its coordinator owns ARView setup and installs postprocess callbacks that route frames through Metal, Core Image, or Metal Performance Shaders effects. |
| Main patterns | UIKit lifecycle composition, UI–RealityKit bridge, Session owner boundary, Delegate callback adapter, Postprocess strategy boundary |
| Project style | Code-rich sample with 19 scanned C/Objective-C header, Metal, Swift file(s) and 1370 implementation line(s). |
Project structure
Source bundle/
└── RealityKitPostProcessing/
├── RealityView/
│ ├── RealityView.swift # RealityView
│ ├── RealityViewContainer.swift # RealityViewContainer, Coordinator
│ └── RealityView+CoachingView.swift
├── AppDelegate.swift # AppDelegate
├── ContentView.swift # ContentView, SelectionCell, ContentView_Previews
└── Application State/
├── ApplicationState+Enums.swift # CategoryEntry, ModeEntry, Category
└── ApplicationState.swift # ApplicationState
Structure observations
- The runtime boundary is SwiftUI/UIKit host; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
- The source is C/Objective-C header, Metal, Swift; role-named types and feature folders separate the major responsibilities.
Overall architecture
flowchart LR
AppDelegate_1["AppDelegate"]
SwiftUI_UIKit_host_2["SwiftUI/UIKit host"]
RealityView_3["RealityView"]
Coordinator_4["Coordinator"]
RealityKit_entity_graph_5["RealityKit entity graph"]
ARView_postprocess___GPU_effects_6["ARView postprocess + GPU effects"]
AppDelegate_1 --> SwiftUI_UIKit_host_2
SwiftUI_UIKit_host_2 --> RealityView_3
RealityView_3 --> Coordinator_4
Coordinator_4 --> RealityKit_entity_graph_5
RealityKit_entity_graph_5 --> ARView_postprocess___GPU_effects_6
Reference code
RealityKitPostProcessing/AppDelegate.swift:12 — executable or app-lifecycle anchor.
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
}The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It separates lifecycle, presentation, stable state, framework/session work, and the RealityKit entity graph.
Ownership and state
classDiagram
RealityView --> SKRenderer : skRenderer
AppDelegate --> UIWindow : window
RealityView --> MTLTexture : alphaTexture
RealityView --> MTLTexture : bloomTexture
Ownership evidence
RealityKitPostProcessing/RealityView/RealityView.swift:48 — representative stored state or nearest verified lifecycle anchor.
var skRenderer: SKRenderer!
// MARK: - Initializers
required init(frame: CGRect) {
super.init(frame: frame)
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
RealityView |
SKRenderer as skRenderer |
stores and coordinates | The owning type coordinates writes |
AppDelegate |
UIWindow as window |
stores and coordinates | The owning type coordinates writes |
RealityView |
MTLTexture as alphaTexture |
stores and coordinates | The owning type coordinates writes |
RealityView |
MTLTexture as bloomTexture |
stores and coordinates | The owning type coordinates writes |
Ownership is intentionally narrow: environment, bindings, observed objects, weak links, and delegate callbacks do not prove lifetime ownership. Initialized stored services and wrapper-managed state do; entities added inside a RealityKit content closure belong to that entity graph.
Class and protocol design
| Type | Responsibility | Depends on or conforms to |
|---|---|---|
AppDelegate (RealityKitPostProcessing/AppDelegate.swift:12) |
Declares app lifecycle and top-level dependency lifetime. | UIResponder, UIApplicationDelegate |
RealityView (RealityKitPostProcessing/RealityView/RealityView.swift:14) |
Presents UI and forwards gestures or lifecycle events. | ARView |
Coordinator (RealityKitPostProcessing/RealityView/RealityViewContainer.swift:34) |
Coordinates long-lived framework or cross-view work. | NSObject |
ContentView (RealityKitPostProcessing/ContentView.swift:15) |
Presents UI and forwards gestures or lifecycle events. | View |
The reviewed source defines no local substitution protocol. Its App, View, delegate, renderer, ARKit, and RealityKit conformances are framework-facing contracts, so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private func postProcessUsingMetalShader(context: ARView.PostProcessCon... (RealityKitPostProcessing/RealityView/RealityView+Metal.swift:105) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
public init() { (RealityKitPostProcessing/RealityView/RealityViewContainer.swift:17) |
public |
The symbol is available to importing modules, subject to its containing type’s visibility. | Inference: Expose an intentional package or cross-target surface. |
No reviewed Swift access example uses fileprivate, private(set), open; declarations without a modifier are internal.
Objective-C/C header and implementation visibility is documented separately from Swift lexical access control.
Reference code
RealityKitPostProcessing/RealityView/RealityView+Metal.swift:105 — representative visibility boundary.
private func postProcessUsingMetalShader(context: ARView.PostProcessContext,
pipeline: MTLComputePipelineState,
parameterHandler: MetalParameterSetup?) {
// ...
}Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | AppDelegate |
The executable boundary determines app, controller, scene, or command lifetime. |
| Presentation and user intent | RealityView |
The view/controller forwards input and owns only presentation-local work. |
| Framework/session/render coordination | Coordinator |
The role-named collaborator isolates long-lived or per-frame work from presentation code. |
| GPU and low-level resource work | RealityKitPostProcessing/RealityView/RealityView.swift:64 |
Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| UIKit lifecycle composition | RealityKitPostProcessing/AppDelegate.swift:12 |
Uses the application delegate and view controller as the explicit lifecycle and scene-ownership boundary. |
| UI–RealityKit bridge | RealityKitPostProcessing/RealityView/RealityView.swift:121 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Session owner boundary | RealityKitPostProcessing/RealityView/RealityViewContainer.swift:34 |
Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller. |
| Delegate callback adapter | RealityKitPostProcessing/AppDelegate.swift:12 |
Inverts imperative framework callbacks into the sample’s owning controller, coordinator, or model. |
| Postprocess strategy boundary | RealityKitPostProcessing/RealityView/RealityView.swift:38 |
Lets one coordinator choose and run interchangeable GPU image effects after RealityKit renders the frame. |
Naming conventions
- Role suffixes are evidence, not decoration: Coordinator:
Coordinator; View:ContentView,RealityView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
postProcessSetupCallback,configureWorldTracking,setUpCoachingOverlay,loadScene,setupPostProcessing,postProcess,postEffectNone,application. - Files and folders use primary-type or responsibility names such as
Views,Models,Rendering,Components,Systems,Packages, or feature names where those boundaries exist.
Architecture takeaways
- Treat
AppDelegateas the owner of executable or scene lifecycle, not automatically as the owner of every entity created later. - Keep view/controller input near presentation, but move sessions, reconstruction, capture, rendering, shared game state, or documents into stable owners when their lifetime exceeds one callback or render pass.
Source map
| Source file | Relevant symbols |
|---|---|
RealityKitPostProcessing/RealityView/RealityView.swift:14 |
RealityView |
RealityKitPostProcessing/AppDelegate.swift:12 |
AppDelegate |
RealityKitPostProcessing/ContentView.swift:15 |
ContentView, SelectionCell, ContentView_Previews |
RealityKitPostProcessing/Application State/ApplicationState+Enums.swift:11 |
CategoryEntry, ModeEntry, Category, Mode |
RealityKitPostProcessing/RealityView/RealityViewContainer.swift:15 |
RealityViewContainer, Coordinator |
RealityKitPostProcessing/RealityView/RealityView+CoachingView.swift:1 |
Feature implementation |
RealityKitPostProcessing/Application State/ApplicationState.swift:11 |
ApplicationState |