Purpose: This prefab provides an optimized solution for a mirror that can dynamically control which layers are reflected without utilizing multiple mirror GameObjects. This method is beneficial for performance optimization, as it minimizes objects stored in memory and reduces the number of objects requiring complex shadow and lighting interactions.
Requirements: The Mirror Prefab REQUIRES ProBuilder to be installed into unity BEFORE the prefab is placed in the world.
Details: Unity 2022.3.22f1, VRChat SDK - Worlds 3.9.0, VRChat Package Resolver Tool 0.1.29, VRChat SDK - Base 3.9.0, ProBuilder 5.2.4.
Last Edit: 14 November 2025 (Baste~)
Download Button (Google Drive)
Step 1. Open your Unity Project
Step 2. Click "Window" at the top of your Unity Window
Step 3. Click "Package Manager"
This should open a new window called "Package Manager"
Step 4. At the top left of the Package Manager window click on the "Packages:" rectangular button
Step 5. Select "Unity Registry"
Step 6: At the top right of the Package Manager search for ProBuilder
Step 7: On the left side of the Package Manager window select "ProBuilder"
Step 8. In the top right of the Package Manager window you should now see "Install, click it
Step 1. Download Unity File above
Step 2. Open Unity Project that you want to add this prefab to
Step 3. Drag the file from where it is stored into your Unity window
This should open a "Import Unity Package" window
Step 4. Click "Import" at the bottom right of the Import Unity Package window
Step 5. If prompted install TMP Essentials
In your "Assets" you should now see a "Baste Assets" folder. Inside this is the "VRChat Mirror Prefab" folder. Inside that is the "VRC Mirror" prefab which you can now drag into your "Scene" Or "Hierarchy" to include into your world.
The Optimized Mirror prefab is designed to efficiently manage mirror functionality using a core set of components, minimizing the need for multiple mirror GameObjects.
The prefab is composed of the following main elements and materials:
Mirror (GameObject): The core object that holds the reflection logic.
Mirror Reflection Material: The material used for the actual reflection surface.
VRC Mirror Reflection Script: The script that handles the real-time reflection calculations.
Mirror Menu (GameObject): An object containing the control UI and main logic script.
Background Material: Material for the menu or backdrop.
Frame Material: Material for the mirror's frame.
Mirror Menu Script: The central script that manages the mirror's behavior.
The prefab relies on three core scripts to provide its full functionality:
VRC Mirror Reflection Script
Location: Attached to the Mirror GameObject.
Function: This is the standard VRChat script that performs the actual mirror reflection. It uses a LayerMask to determine which objects are visible in the reflection.
Mirror Menu Script
Location: Attached to the Mirror Menu GameObject.
Function: This is the central controller script. It holds the functions necessary to read and write (modify) the LayerMask in the $VRC\ Mirror\ Reflection\ Script$.
Key Variable: It stores a reference to the $VRC\ Mirror\ Reflection\ Script$ in a variable called Target Mirror to ensure it's modifying the correct component.
Mirror Toggle Script
Location: Attached to each interactive Button within the Mirror Menu.
Function: Acts as an intermediary, triggering the layer modification functions within the $Mirror\ Menu\ Script$.
Required Inputs (4):
The location of the Mirror Menu.
The layer number you wish to toggle on or off.
An On Material (for when the layer is visible/active).
An Off Material (for when the layer is hidden/inactive).
VRChat SDK Assets (Provided by VRChat)
These assets are standard and are required for basic mirror functionality:
Mirror Reflection Material: Packages/com.vrchat.base/Runtime/VRCSDK/Sample Assets/Materials/MirrorReflection.matVRC
Mirror Reflection Script: Packages/com.vrchat.worlds/Runtime/VRCSDK/Plugins/VRCSDK3.dll
Custom Assets
All custom scripts and materials are found within the "VRChat Mirror Prefab" folder that you imported.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using System;
public class MirrorMenu : UdonSharpBehaviour
{
//Create a variable for our VRC Mirror script that we will be modifying.
public VRC_MirrorReflection TargetMirror;
//The number of bits present in the LayerMask of the VRC Mirror Script.
private const int BitCount = 32;
public void ToggleLayer(int Layer)
{
// The bit mask is calculated by shifting 1 to the left by the TargetLayerIndex. so if we are editing layer 9 (player) it would be //100000000 (512 base 10)
int bitMask = 1 << Layer;
// Get the current integer value of the LayerMask
int currentMaskValue = TargetMirror.m_ReflectLayers.value;
// Use the XOR operator (^) to flip the bit designated by bitMask from Layer.
int newMaskValue = currentMaskValue ^ bitMask;
// Apply the new value back to the mirror's reflect layers
TargetMirror.m_ReflectLayers = newMaskValue;
}
public bool MaskedState(int Layer)
{
// If the result of (current mask AND layer's bit-mask) is non-zero, the layer is enabled.
return (TargetMirror.m_ReflectLayers.value & (1 << Layer)) != 0;
}
//I was able to get rid of soooo much code i'm leaving this comment here to celebreate :)
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class MirrorToggle : UdonSharpBehaviour
{
//Variable to store and link the location of our mirror menu script
public MirrorMenu MirrorMenu;
[Range(0, 31)] // Sets the range from 0 to 100
public int LayerToggled;
//Stores a variable for an "On" Material
public Material OnMaterial;
//Stores a variable for an "Off" Material
public Material OffMaterial;
[HideInInspector]//Hides next variable from Unity Inspector
//Variable to hold if our button is toggled or not
public bool ButtonState;
[HideInInspector]//Hides next variable from Unity Inspector
//References our Gameobject material/render so we can change it
public Renderer ItemRenderer;
void Start()
{
//Grabs out material/renderer so we can change them
ItemRenderer = gameObject.GetComponent<Renderer>();
//Checks if this layer is active
ButtonState = MirrorMenu.MaskedState(LayerToggled);
//Changes material of button to show if layer is enabled or not
if (ButtonState)
{
ItemRenderer.material = OnMaterial;
} else {
ItemRenderer.material = OffMaterial; }
}
void Interact()
{
//Sends this button's layer to be toggled
MirrorMenu.ToggleLayer(LayerToggled);
//Flips button state to match at the same time layer state is changed
ButtonState = !ButtonState;
//Changes material to match button state
if (ButtonState)
{
ItemRenderer.material = OnMaterial;
}
else
{
ItemRenderer.material = OffMaterial;
}
}
}