Develop
Develop
Select your platform

Mixed Reality Utility Kit – Instant placement with Environment Raycasting API

Updated: Nov 6, 2025
One of the most important use cases of MR is being able to place content around your space. Instant Content Placement uses environment raycasting to provide quick placement based on live depth data. This guide covers both the underlying EnvironmentRaycastManager functionality and a step-by-step placement workflow.

Learning Objectives

  • Understand the concept of Environment Raycasting.
  • Learn about the EnvironmentRaycastManager component (raycasts, box placement, overlap checks).
  • Implement a script to place an object into your space.

Requirements & Limitations

  • Platform Support: Meta Quest 3/3S, Unity 2022.3 LTS or later, Oculus XR Plugin OR Unity OpenXR Meta (when using Unity 6)
  • Meta XR SDK: Available in MRUK version 71 or later
  • Field of View: Only rays within the headset’s depth camera frustum will return hits; outside rays yield HitPointOutsideOfCameraFrustum or NoHit.
For versions older than 81 of the sdk
As of v81 of MRUK, the Depth API is no longer needed for this feature. For older versions, you still need the EnvironmentDepthManager in the scene. Unity’s OpenXR plugin (as opposed to the OculusXR plugin) only supports the Depth API in Unity 6, and additionally requires the Meta OpenXR package (com.unity.xr.meta-openxr@2.1.0) to be installed.

Raycasting API Essentials

Before you jump into placing content, it’s essential to understand the foundational queries that drive environment‐aware placement:
  • Raycast: Shoots a virtual ray into the live depth buffer and reports the first intersection point. You’ll use this for pinpointing where on a surface to anchor your content.
  • PlaceBox: Aligns an axis-aligned box (e.g., the bounds of a UI panel or furniture) with a detected flat surface, then checks whether that box would fit without colliding with real-world geometry.
  • CheckBox: Performs an overlap test for a proposed bounding box, letting you quickly reject placements that would intersect walls, tables, or other obstacles.
  • IsSupported: Returns true only on devices and runtimes where environment raycasting is available, so you can safely disable or fallback on unsupported platforms.
// Cast a single ray against live depth data
bool Raycast(Ray ray, out EnvironmentRaycastHit hitInfo, float maxDistance = 100f);

// Try to place an axis-aligned box on a flat surface
bool PlaceBox(Ray ray, Vector3 boxSize, Vector3 upwards, out EnvironmentRaycastHit hitInfo);

// Check if an axis-aligned box overlaps the environment
bool CheckBox(Vector3 center, Vector3 halfExtents, Quaternion orientation);

// True if environment raycasts are supported on this device
public static bool IsSupported { get; }
Environment Raycast Demo
Raycast
A raycast returns an EnvironmentRaycastHit struct containing point, normal and normalConfidence, along with a status enum that conveys errors (NotReady, NotSupported, Occluded, etc.).
PlaceBox Demo
PlaceBox
Use PlaceBox to align a box on a flat area: it adjusts orientation based on upwards, then checks for collisions to ensure the box fits. CheckBox returns overlap only.

Content Placement with Raycasting API

Below is a concise, step-by-step integration of the Raycasting API into a Unity Project. First we dissect the core operations, then show the complete script in a collapsible section.
Before we begin, make sure you’ve covered the steps in Getting Started.

1. Scene Setup

  1. Follow the basic Getting Started guide.
  2. Create an empty GameObject and add the EnvironmentRaycastManager components to it.
  3. Open the Project Setup Tool (PST) and fix all outstanding warnings.
  4. Create a new script called InstantPlacementController and add it to your GameObject.
For versions older than 81 of the sdk
As of v81 of MRUK, the Depth API is no longer needed for this feature. For older versions, you need to add the EnvironmentDepthManager to the scene.

2. Initialize all references

On the InstantPlacementController ensure that all variables are referenced in the inspector.
  • rightControllerAnchor: This is the origin of our raycast. In this case we use the transform of the right hand, but you can use any other transform. The right hand anchor can be found in the OVRCameraRig GameObject under TrackingSpace.
  • prefabToPlace: This is the prefab that will be instantiated when the raycast hits a surface. In this case we use a simple cube, but you can use any other prefab.
  • raycastManager: This is the EnvironmentRaycastManager component that we added to the GameObject in the previous step.
using Meta.XR;
using UnityEngine;

public class InstantPlacementController : MonoBehaviour
{
    public Transform rightControllerAnchor;
    public GameObject prefabToPlace;
    public EnvironmentRaycastManager raycastManager;
}

3. Construct Ray on Input

We construct a ray to determine an origin and direction for our raycast. In this case we take the anchor of our right hand and construct a ray as soon as the right index trigger is pressed down.
private void Update()
{
    if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
    {
        var ray = new Ray(
            rightControllerAnchor.position,
            rightControllerAnchor.forward
        );

        TryPlace(ray);
    }
}

4. Perform Raycast & Instantiate

We raycast using the constructed ray, and if a surface is hit in the physical space, we continue to instantiate the prefab (e.g. a simple cube) and set its position to the hit point, as well as its rotation to the hit point’s normal and facing away from it.
private void TryPlace(Ray ray)
{
    if (raycastManager.Raycast(ray, out var hit))
    {
        var objectToPlace = Instantiate(prefabToPlace);
        objectToPlace.transform.SetPositionAndRotation(
            hit.point,
            Quaternion.LookRotation(hit.normal, Vector3.up)
        );

        // If no MRUK component is present in the scene, we add an OVRSpatialAnchor component
        // to the instantiated prefab to anchor it in the physical space and prevent drift.
        if (MRUK.Instance?.IsWorldLockActive != true)
         {
                 objectToPlace.AddComponent<OVRSpatialAnchor>();
         }
    }
}
Note
If an MRUK component is present in the scene and World Locking is enabled, we do not need to add a OVRSpatialAnchor component.

☕ Full Instant Content Placement Script


using Meta.XR.MRUtilityKit;
using UnityEngine;

public class InstantPlacementController : MonoBehaviour
{
    public Transform rightControllerAnchor;
    public GameObject prefabToPlace;
    public EnvironmentRaycastManager raycastManager;

    private void Update()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
        {
            var ray = new Ray(
                rightControllerAnchor.position,
                rightControllerAnchor.forward
            );

            TryPlace(ray);
        }
    }

    private void TryPlace(Ray ray)
    {
        if (raycastManager.Raycast(ray, out var hit))
        {
            var objectToPlace = Instantiate(prefabToPlace);
            objectToPlace.transform.SetPositionAndRotation(
                hit.point,
                Quaternion.LookRotation(hit.normal, Vector3.up)
            );

            if (MRUK.Instance?.IsWorldLockActive != true)
            {
                objectToPlace.AddComponent<OVRSpatialAnchor>();
            }
        }
    }
}


Instant Content Placement MR Motif
Content Placement MR Motif
The Instant Content Placement scene shows Depth Raycasting to detect surfaces and a custom shader that renders a shadow below the object, cutting off at real geometry edges for realistic immersion without manual scanning.
View sample
Environment Panel Placement
This sample demonstrates Environment Raycasting (Beta) to place UI panels in your environment. It casts a ray from the controller and, on a vertical-hit normal, uses PlaceBox to stick the panel. If no vertical surface is hit, CheckBox ensures the panel won’t intersect real-world geometry.
View sample
Environment Panel Placement Sample

Box Placement & Collision Checks

In addition to single‐ray hits, you can use PlaceBox to align and fit a box (e.g. a panel, furniture bounds) onto a flat surface, and CheckBox to preflight collision checks without instantiating anything.

Example: PlaceBox

Place a 0.5 × 0.1 × 0.3 m panel flush on a detected surface:
// Cast a ray from your controller
var ray = new Ray(controller.position, controller.forward);

// Define the panel’s local size (width, height, depth)
Vector3 panelSize = new Vector3(0.5f, 0.1f, 0.3f);

// 'up' keeps the panel upright after placement
Vector3 up = Vector3.up;

if (_raycastManager.PlaceBox(ray, panelSize, up, out var hitInfo))
{
    // Instantiate and orient the panel at the hit point
    var panel = Instantiate(panelPrefab);
    panel.transform.SetPositionAndRotation(
        hitInfo.point,
        Quaternion.LookRotation(hitInfo.normal, up)
    );
}
else
{
    Debug.Log("No suitable flat area found for panel.");
}

Example: CheckBox

Verify that a 1 × 1 × 1 m cube won’t overlap real geometry before spawning:
// Desired spawn position and cube half‑extents
Vector3 spawnPos = hitInfo.point;
Vector3 halfExtents = Vector3.one * 0.5f;

// Keep cube axis‑aligned
Quaternion orientation = Quaternion.identity;

if (_raycastManager.CheckBox(spawnPos, halfExtents, orientation))
{
    Debug.Log("Cube overlaps environment—choose a different spot.");
}
else
{
    Instantiate(cubePrefab, spawnPos, orientation);
}

Previous:Getting Started
Explore more MRUK documentation topics to dive deeper into spatial queries, content placement, manipulation, sharing, and debugging.
Core Concepts
  • Overview Get an overview of MRUK’s key areas and features.
  • Getting Started Set up your project, install MRUK, and understand space setup with available Building Blocks.
  • Manage Scene Data Work with MRUKRoom, EffectMesh, anchors, and semantic labels to reflect room structure.
Content & Interaction
Multiuser & Debugging
MRUK Samples & Tutorials
Mixed Reality Design Principles
Did you find this page helpful?
Thumbs up icon
Thumbs down icon