visionOS: immersive rendering and tracking
Application role, Metal/Mobile, tracking authorization, near plane and export limitations.
visionOS: immersive rendering and tracking
Reckfell has a native visionOS XR module using Metal, Compositor Services and ARKit. It is a platform-specific integration, separate from OpenXR and browser WebXR. Its registered interface name is visionOS, with that exact casing.
Current C# limitation: the C# exporter in 1.0.0 does not implement visionOS as a target. The VisionOSXRInterface reference is source-only in the installed Windows SDK. A native visionOS exporter/module in source does not establish a working C# export, signed application or device build. Validate your template, scripting configuration and Apple toolchain before committing to this target.
Configure the application mode first
The export preset contains application/app_role: Window=0 or Immersive=1. The default is Window. Adding an XROrigin3D does not turn a windowed application into a Compositor Services application.
For the immersive path, select Immersive and configure application/immersion_style: Full=0, Mixed=1 or Progressive=2; the preset default is Mixed. The Swift layer and export metadata must agree with the intended application role. Full replaces the surroundings; Mixed blends virtual content with them. Progressive is a separate immersion choice, not dynamic render quality.
The current Compositor Services path requires the Metal driver. Display-server code changes Forward+ to Mobile in this mode. Configure and assess the scene with Mobile rather than relying on desktop-only Forward+ effects. A simulator or desktop preview is not evidence of device rendering.
The Swift compositor config selects rgba16Float color, depth32Float_stencil8 depth and layered layout. It enables foveation if the device capabilities support it; a missing layered layout is a fatal configuration condition. These are platform decisions, not instructions to create and free the compositor's textures yourself.
Scene and startup
Use the XR scene setup for the origin/camera hierarchy and XR shaders. The minimal hierarchy is:
XROrigin3D
└── XRCamera3DOn the native visionOS target, find the registered visionOS interface through XRServer. Keep one owner for initialization and viewport state; do not initialize OpenXR alongside it. Retain the original viewport configuration so it can be restored on a failed start or exit.
For the immersive viewport, configure UseXR, UseHdr2D and XR VRS deliberately. The platform exposes a rasterization-rate-map VRS format, not a hand-authored ordinary shading-rate texture. Use the active XRCamera3D for projection and verify it is the viewport's current camera.
Initialize returning true does not prove granted tracking permissions, a valid pose or a displayed stereo frame. Current initialization starts ARKit and requests hand/accessory authorization asynchronously. It also calls CompositorServicesData.initialize without checking that function's boolean result. Inspect native errors and actual frame/tracking readiness; do not use Initialize alone as an acceptance test. This is a source observation, not an engine fix or reproduced device failure.
Hand and controller tracking
Both project settings default to false:
| Project setting | Export privacy text |
|---|---|
| xr/visionos/enable_hand_tracking | privacy/hand_tracking_usage_description → NSHandsTrackingUsageDescription |
| xr/visionos/enable_controller_tracking | privacy/accessory_tracking_usage_description → NSAccessoryTrackingUsageDescription |
Enable only the tracking your application uses and explain its purpose in the corresponding export field. A checked setting and a created tracker do not establish authorization: providers become active only when enabled and allowed. Denial is reported in native logs; offer an input fallback instead of leaving the user unable to continue.
Hand trackers use /user/hand_tracker/left and /user/hand_tracker/right. Bind XRHandModifier3D to the intended hand and check current tracking/joint validity before driving gameplay. A modifier or visible hand mesh is not proof of current ARKit data. Controllers use a separate accessory-tracking path; do not treat any connected gamepad as a tracked spatial controller automatically. Borrowed provider trackers must not be Free/Dispose'd by gameplay.
Near plane and world scale
The engine reads supportedMinimumNearPlaneDistance from layer capabilities. Its projection guard checks:
camera Near / XRServer.WorldScale >= platform minimum
Choose a positive world scale and allow a small numerical margin. For a platform minimum of 0.1 m and WorldScale=2, Near=0.201 satisfies the condition; Near=0.1 does not. The 0.1 value is an example, not a universal capability constant. Check the actual current camera: changing an unused camera will not fix projection errors.
Mixed immersion: alpha and depth together
Compositor Services combines color and depth, using premultiplied alpha and reverse-Z conventions. For a transparent mixed background, begin with black RGB and alpha 0; colored RGB at alpha 0 is not a safe transparent-background recipe. Opaque virtual surfaces need valid depth. See Apple's Metal passthrough explanation.
Do not repair missing content by forcing every transparent particle or invisible surface to write opaque depth: that can produce incorrect composition and occlusion. Isolate the problem with an opaque object, then test the material's color, alpha, depth write and background together.
The old depth-only full-screen shader was incomplete without mesh, priority and culling configuration and was not validated on this target. This guide does not present it as a ready fix. If an opaque virtual sky is needed, assess its depth-producing background on the actual device; black transparent background and a visible virtual sky are different requirements.
Quality and diagnosis
xr/visionos/dynamic_render_quality/enable defaults to false; maximum_quality defaults to 0.38. Runtime CurrentRenderQuality requires dynamic quality enabled and must not exceed that configured maximum. It is not a promise of FPS, antialiasing or acceptable image quality. Raise quality only after measuring device frame time and inspecting both eyes.
| Symptom | First check |
|---|---|
| No visionOS interface on Windows | Platform-gated module and source-only reference; not a missing project Entity. |
| C# export rejected | Current C# target support; a native export preset does not add managed runtime support. |
| App opens in a window | application/app_role and exported application metadata. |
| Startup errors or no immersive frame | Metal driver, layer renderer/capabilities, native errors and actual current camera. |
| Projection rejected | Near divided by WorldScale against the reported minimum. |
| Hands remain static or absent | Both enable setting and authorization, current provider data and modifier binding. |
| Halos, missing sky or particles | Premultiplied color/alpha, depth and the actual immersion style. |
| Runtime quality setter fails | Dynamic quality enabled and requested value within the configured maximum. |
| Freeze or termination during loading | Device logs and frame stalls, resource/shader preparation and a launch without a debugger; do not assume one universal timeout. |
Verification scope
This guide was reviewed against current module, export, compositor, projection, authorization and C# exporter source. Platform build-gate truth cases were executed as Python configuration logic only. No visionOS native code, Apple toolchain, simulator, signing, C# export, headset, permissions, render output or frame performance was executed here. No ready-to-run device sample or illustrative capture is claimed. Engine Source, Runtime and SDK were unchanged.