Mixed Reality Utility Kit – Manipulate Scene Visuals
Updated: Sep 18, 2025
Learning Objectives
UseEffectMesh to cut holes in scene geometry (doors, windows, ceilings).
Configure the DestructibleGlobalMeshSpawner for runtime fragmentation and destruction.
Apply the Selective Passthrough shader to hide or reveal real-world surfaces dynamically.
Implement a ceiling-reveal workflow via custom scripts or prefab spawners.
Overview
Beyond placing content, MRUK lets you reshape and visualize the real-world scene itself. You can cut out parts of walls or ceilings, spawn destructible fragments, or use passthrough shaders to reveal portals into VR or AR.
Cutting Holes with EffectMesh
The EffectMesh prefab can cut out semantic labels like doors, windows and ceilings by excluding them from the generated mesh. In the inspector, under Cut Holes, select any labels you wish to remove, creating a hole in that real-world surface, such as CEILING or WINDOW_FRAME.
Destructible Global Mesh
Spawn a runtime, breakable version of the entire Scene Mesh. Fragments can be independently destroyed or manipulated for dramatic effects.
Segments Density: Control fragment count vs. performance.
Material: Assign custom materials per fragment.
Each fragment is a separate GameObject with a DestructibleMeshComponent for debug controls in-Editor. Add DestructibleGlobalMeshSpawner to your SceneMesh; configure reserved volumes, segment density, and materials to get a fully breakable environment.
Selective Passthrough Shader
The Meta Core SDK’s Selective Passthrough shader and material makes a mesh invisible to reveal the real-world camera feed. Apply it to any EffectMesh or custom mesh to carve portals into the physical environment. Assign the Selective Passthrough material to Mesh Material on your EffectMesh and all generated surfaces become passthrough.
Tutorial: Ceiling Sky Reveal
In this step-by-step tutorial, you will learn how to slowly remove the ceiling to reveal a virtual sky behind it, which is a popular use cases in Mixed Reality. When moving parts of your room, it is best practice to not move the MRUK anchor itself but instead move the EffectMesh.
There are at least two common approaches:
Option A: Use MRUK and EffectMesh utility functions
On the EffectMesh component, set Mesh Material to a material with the Selective Passthrough shader.
Write a script that waits for the EffectMesh to create the ceiling object, locate the ceiling anchor and move it out of view at runtime:
using System.Collections;
using Meta.XR.MRUtilityKit;
using UnityEngine;
public class CeilingMover : MonoBehaviour
{
[Tooltip("Speed at which the ceiling moves out")]
[SerializeField] private float speed = 0.5f;
[Tooltip("The EffectMesh spawning our ceiling")]
[SerializeField] private EffectMesh ceilingEffectMesh;
private void Start()
{
StartCoroutine(WaitForEffectMeshAndMove());
}
private IEnumerator WaitForEffectMeshAndMove()
{
// 1. grab the current room
var room = MRUK.Instance.GetCurrentRoom();
// 2. wait until the mesh is spawned
while (room.CeilingAnchor &&
!ceilingEffectMesh.EffectMeshObjects.ContainsKey(room.CeilingAnchor))
{
yield return null;
}
// 3. fetch the wrapper and its real mesh GameObject
var wrapper = ceilingEffectMesh.EffectMeshObjects[room.CeilingAnchor];
var meshGo = wrapper.effectMeshGO;
if (!meshGo)
{
Debug.LogError("No effectMeshGO found on wrapper!");
yield break;
}
// 4. detach from the anchor so we only move the mesh
meshGo.transform.SetParent(null, /* worldPositionStays= */ true);
// 5. compute how far to move: half-room + half-ceiling + margin
var roomBounds = room.GetRoomBounds();
var meshBounds = meshGo.GetComponentInChildren<Renderer>().bounds;
const float MARGIN = 0.1f;
var distance = roomBounds.extents.x + meshBounds.extents.x + MARGIN;
// 6. slide the mesh straight out in world-space
var moved = 0f;
var dir = Vector3.right;
while (moved < distance)
{
var step = speed * Time.deltaTime;
meshGo.transform.Translate(dir * step, Space.World);
moved += step;
yield return null;
}
}
}
Attach this component to your scene and assign the EffectMesh component in your scene that is responsible for creating the ceiling mesh.
Press play in your editor and see how the room gets created (appears black) and the ceiling moves out of the room’s bounds.
Option B: Use EffectMesh and override the AnchorPrefabSpawner
Set Mesh Material on EffectMesh to Selective Passthrough and deselect CEILING in the Labels property.
Create a custom spawner to size a prefab to the ceiling’s bounds by inheriting from the AnchorPrefabSpawner:
using Meta.XR.MRUtilityKit;
using UnityEngine;
public class CeilingPrefabSpawner : AnchorPrefabSpawner
{
public override Vector2 CustomPrefabScaling(Vector2 localScale)
{
return new Vector2(localScale.x, localScale.y);
}
}
In your scene, add this spawner and under Prefabs to Spawn add a new list item. Select the CEILING label under Labels, and assign your ceiling prefab under Prefabs. For the Scaling property, select Custom, so our script can override it.
Move or deactivate the spawned prefab at runtime to unveil the sky.
Tip: When to Use SceneLoaded Callbacks
MRUK provides two ways to react when your scene data is ready:
RegisterSceneLoadedCallback: Use this helper if you want your callback invoked immediately and on all future loads. It will fire instantly if the scene is already initialized.
SceneLoadedEvent.AddListener: Use this when you only need to listen from this point forward. If the scene has already loaded, your listener will not be called until the next load.
using Meta.XR.MRUtilityKit;
using UnityEngine;
private void Awake()
{
MRUK.Instance.RegisterSceneLoadedCallback(OnSceneReady);
MRUK.Instance.SceneLoadedEvent.AddListener(OnNextSceneReady);
}
// 1. Runs now (if loaded) and on every future load
private void OnSceneReady()
{
Debug.Log("Scene is ready right now or was already ready!");
}
// 2. Runs only the next time the scene loads
private void OnNextSceneReady()
{
Debug.Log("This runs only the next time the scene loads.");
// If you want it only once, remove the listener:
MRUK.Instance.SceneLoadedEvent.RemoveListener(OnNextSceneReady);
}
If you have dependencies between MonoBehaviours, for example a custom script which depends on Physics colliders spawned by EffectMesh, ensure that you register the callbacks in the same order you want to receive them. Unity allows you to change the Script Order Execution which can help ensure callbacks are registered in the desired order.
Related Samples
DestructibleMesh (Beta)
Demonstrates the DestructibleGlobalMeshSpawner component. A DestructibleMesh prefab segments and spawns the global mesh when a room is created, parenting it under the GLOBAL_MESH anchor. Each segment is a GameObject with a MeshFilter and MeshRenderer.
Reconstructs an interior scene using furniture prefabs for each semantic label via AnchorPrefabSpawner. Furniture uses GridsliceResizer to preserve aspect ratios. EffectMesh generates floor and ceiling meshes with carpet and ceiling textures, using Metric UVs for 1 m texture repeats.