A component that turns mesh surfaces into portals to a different world.
A RealityKit portal defines a way to look into a different, immersive world. You define an entity as a portal
when it also has a ``ModelComponent`` that contains a mesh part with ``PortalMaterial``.
To create a portal, set its ``targetEntity`` property to an entity with a
``WorldComponent``. Entities under that world only render within the portal.
### Quick start
On iOS 27, macOS 27, tvOS 27, visionOS 27, and later, ``makePortal(surfaceStyle:boundaryStyle:boundaryMode:)``
creates a configured portal entity and its target world entity in a single call:
```swift
let portal = PortalComponent.makePortal(
surfaceStyle: .init(width: 0.5, height: 0.5),
boundaryStyle: .infinitePlane(),
boundaryMode: .clippingAndCrossing
)
content.add(portal.worldEntity)
content.add(portal.portalEntity)
```
To configure existing entities, use
``configure(world:portalEntity:surfaceStyle:boundaryStyle:boundaryMode:)``.
### Manual construction
On earlier OS releases, or when you need direct control over the portal's mesh, material, or
boundary geometry, set the components yourself:
```swift
let world = Entity()
world.components.set(WorldComponent())
let portal = Entity()
portal.components.set(
ModelComponent(
mesh: .generatePlane(width: 0.5, height: 0.5, cornerRadius: 0.1),
materials: [PortalMaterial()]
)
)
portal.components.set(PortalComponent(target: world))
content.add(world)
content.add(portal)
```
### Clipping and Crossing
You can enable clipping by configuring ``clippingMode-swift.property`` to something
other than ``ClippingMode-swift.enum/disabled``.
For example, you can use ``ClippingMode-swift.enum/plane(_:)`` to clip portal content to an
infinite half-space, or ``ClippingMode-swift.enum/volume(_:)`` to clip to a box-bounded
region of space. Clipping prevents portal world content from rendering beyond
the portal boundary and causing depth confusion.
Entities inside the portal world with a ``PortalCrossingComponent`` can freely
cross in and out of the portal boundary in any of the following platforms:
- iOS 18 and later
- macOS 15 and later
- visionOS 2 and later
You can enable the crossing feature by configuring ``crossingMode-swift.property``
to something other than ``CrossingMode-swift.enum/disabled``.
Such as ``CrossingMode-swift.enum/plane(_:)`` for an infinite-plane crossing, or
``CrossingMode-swift.enum/volume(_:)`` for a box-bounded crossing.
```swift
let world = Entity()
world.components.set(WorldComponent())
// Create an entity that doesn't cross beyond the portal bounds.
let notCrossing = Entity()
// Create an entity that crosses beyond the portal bounds.
let willCross = Entity()
willCross.components.set(PortalCrossingComponent())
world.addChild(notCrossing)
world.addChild(willCross)
// Set up a crossable portal, without a near clip.
let portal = Entity()
portal.components.set(
ModelComponent(
mesh: .generatePlane(width: 0.5, height: 0.5, cornerRadius: 0.1),
materials: [PortalMaterial()]
)
)
var portalComp = PortalComponent(target: world)
portalComp.clippingMode = .plane(.positiveZ)
portalComp.crossingMode = .plane(.positiveZ)
portal.components.set(portalComp)
content.add(world)
content.add(portal)
```
The spaceships below have a ``PortalCrossingComponent``.
@Video(
source: portal-crossing-video,
alt: "Four videos of space ships poking out of portals looking into outer space. Each video is in a grid formation. The top left video have both clipping and crossing mode disabled, so the space ship is being bounded by the portal geometry. The bottom left video have clipping mode set to plane and crossing mode disabled, so the space ship is clipped by the portal plane. The two videos on the right column have crossing mode enabled, so the spaceship is rendered outside the portal geometry.",
poster: portal-crossing-video-poster
)
The spaceships below *don't* have a ``PortalCrossingComponent``.
@Video(
source: portal-clipping-video,
alt: "Four videos of space ships poking out of portals looking into outer space. Each video is in a grid formation. The two videos on the top row have clipping mode disabled so the space ships are bounded by the portal geometry. The two videos on the bottom row have clipping mode enabled so the space ships are clipped by the portal plane.",
poster: portal-clipping-video-poster
)
### Lighting
You define the lighting in a portal world with ``ImageBasedLightComponent`` and
``ImageBasedLightReceiverComponent``.
RealityKit provides a default IBL if you don't specify one with ``ImageBasedLightReceiverComponent``.
Contents within a portal world don't receive real-world probe lighting. However, you can achieve a similar
effect in the portal world using ``VirtualEnvironmentProbeComponent``.
You can configure this virtual probe lighting contribution with
``EnvironmentLightingConfigurationComponent``.
Dynamic lights, such as ``PointLightComponent`` and ``DirectionalLightComponent``,
don't cross world bounds.
Different lighting environments light the portal crossing entities based on which side of the portal they
are on:
- When inside the portal, the portal world's lighting lights the entity.
- When outside the portal, the default world's lighting lights the entity.
Creates a portal entity and a world entity, and configures them.
This is the simplest way to set up a portal. The returned ``Portal`` value contains a fresh
portal entity and a fresh world entity that you add to your scene. To add portal world
content, parent it under the returned `worldEntity`.
```swift
let portal = PortalComponent.makePortal(
surfaceStyle: .init(width: 0.5, height: 0.5),
boundaryStyle: .infinitePlane(),
boundaryMode: .clippingAndCrossing
)
content.add(portal.worldEntity)
content.add(portal.portalEntity)
```
To configure entities you already own — for example, a portal entity that has gesture
components, or a world entity that's already in your scene hierarchy — use
``configure(world:portalEntity:surfaceStyle:boundaryStyle:boundaryMode:)`` instead.
- Parameters:
- surfaceStyle: The size of the portal surface mesh.
- boundaryStyle: The shape of the clipping and crossing boundary. Defaults to
``BoundaryStyle/infinitePlane()``.
- boundaryMode: The combination of clipping and crossing behaviors to enable. Defaults
to ``BoundaryMode/none``.
- Returns: A ``Portal`` containing the new portal entity and world entity.
get · iOS 18.0+
clippingMode
TypeScript
get clippingMode(): PortalComponent_ClippingMode
Swift declaration
var clippingMode: PortalComponent.ClippingMode { get set }
The clipping behavior of the portal component.
Provide a ``ClippingMode-swift.enum`` to configure the clipping behaviors.
set · iOS 18.0+
clippingMode
TypeScript
set clippingMode(value: PortalComponent_ClippingMode)
Swift declaration
var clippingMode: PortalComponent.ClippingMode { get set }
The clipping behavior of the portal component.
Provide a ``ClippingMode-swift.enum`` to configure the clipping behaviors.
get · iOS 18.0+
crossingMode
TypeScript
get crossingMode(): PortalComponent_CrossingMode
Swift declaration
var crossingMode: PortalComponent.CrossingMode { get set }
The crossing behavior of the portal component.
Provide a ``CrossingMode-swift.enum`` to configure the crossing behaviors.
set · iOS 18.0+
crossingMode
TypeScript
set crossingMode(value: PortalComponent_CrossingMode)
Swift declaration
var crossingMode: PortalComponent.CrossingMode { get set }
The crossing behavior of the portal component.
Provide a ``CrossingMode-swift.enum`` to configure the crossing behaviors.
get · iOS 18.0+
targetEntity
TypeScript
get targetEntity(): (Entity) | null
Swift declaration
var targetEntity: Entity? { get set }
The root entity for the portal's target world.
When the target entity is valid and has a ``WorldComponent``,
the portal renders with the target entity and its hierarchy tree.
When the target entity doesn't have a ``WorldComponent``,
the portal doesn't render.
set · iOS 18.0+
targetEntity
TypeScript
set targetEntity(value: (Entity) | null)
Swift declaration
var targetEntity: Entity? { get set }
The root entity for the portal's target world.
When the target entity is valid and has a ``WorldComponent``,
the portal renders with the target entity and its hierarchy tree.
When the target entity doesn't have a ``WorldComponent``,
the portal doesn't render.
get · iOS 18.0+
clippingPlane
TypeScript
get clippingPlane(): (PortalComponent_ClippingPlane) | null
Swift declaration
var clippingPlane: PortalComponent.ClippingPlane? { get set }
The clipping plane of the portal, in the entity's local coordinates.
When you set this property to a non-`nil` value, the portal clips meshes inside the portal
world that are in front of the clipping plane.
This property is a convenience that maps onto the more general ``clippingMode-swift.property``.
Reading it returns a value only when ``clippingMode-swift.property`` is
``ClippingMode-swift.enum/plane(_:)``; for ``ClippingMode-swift.enum/disabled``
or ``ClippingMode-swift.enum/volume(_:)``, it returns `nil`.
For new code, prefer ``clippingMode-swift.property`` directly. It supports
``ClippingMode-swift.enum/volume(_:)`` for box-shaped clipping boundaries and pairs naturally
with ``crossingMode-swift.property``.
set · iOS 18.0+
clippingPlane
TypeScript
set clippingPlane(value: (PortalComponent_ClippingPlane) | null)
Swift declaration
var clippingPlane: PortalComponent.ClippingPlane? { get set }
The clipping plane of the portal, in the entity's local coordinates.
When you set this property to a non-`nil` value, the portal clips meshes inside the portal
world that are in front of the clipping plane.
This property is a convenience that maps onto the more general ``clippingMode-swift.property``.
Reading it returns a value only when ``clippingMode-swift.property`` is
``ClippingMode-swift.enum/plane(_:)``; for ``ClippingMode-swift.enum/disabled``
or ``ClippingMode-swift.enum/volume(_:)``, it returns `nil`.
For new code, prefer ``clippingMode-swift.property`` directly. It supports
``ClippingMode-swift.enum/volume(_:)`` for box-shaped clipping boundaries and pairs naturally
with ``crossingMode-swift.property``.
get · iOS 27.0+
lightingBlendDistance
TypeScript
get lightingBlendDistance(): number
Swift declaration
var lightingBlendDistance: Float { get set }
The distance over which crossing-entity lighting blends between the host scene and the portal world.
As an entity with ``PortalCrossingComponent`` crosses the portal boundary, RealityKit
transitions its lighting from the host scene's environment to the portal world's
environment. This property controls the width, in meters, of that transition:
- A value of `0` produces a sharp lighting boundary, with the entity fully lit by the
portal world on the inside and the host scene on the outside.
- A positive value produces a smooth gradient that spans the given distance into the portal
world, portal crossing entities are unaffected by this value.
The boundary used for the distance calculation is the geometry you configure with
``crossingMode-swift.property``: a plane for ``CrossingMode-swift.enum/plane(_:)``,
or a box for ``CrossingMode-swift.enum/volume(_:)``.
- Note: This property only affects rendering when ``crossingMode-swift.property`` is enabled.
set · iOS 27.0+
lightingBlendDistance
TypeScript
set lightingBlendDistance(value: number)
Swift declaration
var lightingBlendDistance: Float { get set }
The distance over which crossing-entity lighting blends between the host scene and the portal world.
As an entity with ``PortalCrossingComponent`` crosses the portal boundary, RealityKit
transitions its lighting from the host scene's environment to the portal world's
environment. This property controls the width, in meters, of that transition:
- A value of `0` produces a sharp lighting boundary, with the entity fully lit by the
portal world on the inside and the host scene on the outside.
- A positive value produces a smooth gradient that spans the given distance into the portal
world, portal crossing entities are unaffected by this value.
The boundary used for the distance calculation is the geometry you configure with
``crossingMode-swift.property``: a plane for ``CrossingMode-swift.enum/plane(_:)``,
or a box for ``CrossingMode-swift.enum/volume(_:)``.
- Note: This property only affects rendering when ``crossingMode-swift.property`` is enabled.
Creates a portal component with a target entity, clipping mode, and crossing mode.
To render a portal, an entity needs a ``PortalComponent`` and a ``ModelComponent``, using one or
more ``PortalMaterial`` instances.
The target entity needs to have a ``WorldComponent`` or the portal does not render.
Provide ``ClippingMode-swift.enum`` and ``CrossingMode-swift.enum`` to configure the corresponding
clipping and crossing features.
Using this initializer is equivalent to setting ``targetEntity``, ``clippingMode-swift.property`` and
``crossingMode-swift.property`` properties directly.
See ``PortalComponent`` for example usage.
- Parameters:
- target: A target world entity the portal is looking into.
- clippingMode: A configuration for the portal's clipping feature.
- crossingMode: A configuration for the portal's crossing feature.
Creates a portal component with a target entity and an optional clipping plane.
This initializes the ``PortalComponent`` with the given target entity,
and an optional clipping plane.
The target entity needs a ``WorldComponent`` in its component set.
To render a portal, an entity needs a ``PortalComponent`` and a
``ModelComponent``, using one or more ``PortalMaterial`` instances.
This initializer is equivalent to setting ``PortalComponent/clippingMode-swift.property`` with
the corresponding ``PortalComponent/Plane``, leaving ``crossingMode-swift.property``
at ``CrossingMode-swift.enum/disabled``.
For new code, prefer ``init(target:clippingMode:crossingMode:)``, which lets you configure
clipping and crossing together and supports volumetric boundaries.
- Parameters:
- target: A target world entity the portal is looking into.
- clippingPlane: A planar representation of a portal to enable the clipping feature.
When `nil`, ``PortalComponent/clippingMode-swift.property`` is ``ClippingMode-swift.enum/disabled``.
Creates a portal component with a target entity, a single planar definition, and portal options.
Use this initializer to toggle ``clippingMode-swift.property`` and
``crossingMode-swift.property``
with the same ``PortalComponent/Plane``.
For new code, prefer ``init(target:clippingMode:crossingMode:)``, which lets you configure
clipping and crossing independently and supports ``ClippingMode-swift.enum/volume(_:)``
and ``CrossingMode-swift.enum/volume(_:)`` for box-shaped boundaries.
- Parameters:
- target: A target world entity the portal is looking into.
- plane: A plane for configuring clipping and crossing features.
- options: An option set that determines which clipping and crossing features to enable.
Configures an existing pair of entities as a portal and its target world.
This method writes a coordinated set of components onto the two entities you provide. Use it
when you already own both entities — for example, when the portal entity holds gestures or
other components, or when the world entity is a child of an existing scene.
On the world entity, this method sets:
- ``WorldComponent``
On the portal entity, this method sets:
- ``ModelComponent`` with a flat plane mesh sized by `surfaceStyle`, applied with ``PortalMaterial``
- ``PortalComponent`` with ``PortalComponent/clippingMode-swift.property`` and
``PortalComponent/crossingMode-swift.property`` configured from `boundaryStyle` and `boundaryMode`
This method replaces any existing ``WorldComponent``, ``ModelComponent``, or
``PortalComponent`` on the entities. Other components on either entity are preserved.
To create both entities at once, use
``makePortal(surfaceStyle:boundaryStyle:boundaryMode:)`` instead.
- Parameters:
- world: The entity that contains the portal's content. Place portal world descendants
under this entity.
- portalEntity: The entity that displays the portal surface in the host scene.
- surfaceStyle: The size of the portal surface mesh.
- boundaryStyle: The shape of the clipping and crossing boundary. Defaults to
``BoundaryStyle/infinitePlane()``.
- boundaryMode: The combination of clipping and crossing behaviors to enable. Defaults
to ``BoundaryMode/none``.