In VR/AR, occlusion happens when a 3D object blocks the view of another 3D object. Occlusion gives your virtual space a feeling of depth and immersion. Today, we’re going to cover how you can use occlusion as a tool within your own application.

Many of the examples we’ll list use AR, but the following C# code and tips can apply to any 3D app. Our main example is of an X-ray tool showing the bones underneath the skin:

X Ray Bones AR

Three objects are involved in this process. In the example above, we have the X-ray tool, the skin layer, and the bone layer. Each will need its own unique script.

Let’s throw some basics in just to make sure we’re all on the same page. Each object has a mesh filter, which determines what the object is. For example, the mesh filter tells us the object is a cube. A mesh render determines what the object looks like, whether that’s a colour, an opaque texture, or appearing invisible.

Make sense? Then let’s dive in!

Download our 10-week VR/AR Development with Unity Course Syllabus


 

Skip to the Technical Steps

 

Step 1: The Object Blocking The View

 
The object blocking the view in our example is the X-ray tool. It will need to be invisible in order to see objects behind it, so we’ll need to add an invisible mask to it.

First, set up your Unity project as usual. We’ll be working with SteamVR. After you have your objects created — we have a cube representing the X-ray tool, a cube representing skin, and a circle representing bones — we’re good to move forward.

You’ll need to make a new surface shader and title it “InvisibleMask.” Then, add in the following:

Shader "Custom/InvisibleMask"	//make sure you named your shader file InvisibleMask
{
  SubShader
  {
    Tags { "Queue"="Transparent+1" 
  }    // renders after all transparent objects (queue = 3001)

This script orders the render queue after all opaque objects, which are usually set at 2001. It’s a general C# ordering rule that opaque objects render at 2000+, and transparent objects render at 3000+. All this determines is where your object lines up in the rendering queue.

The next bit of script including ‘Blend Zero One’ makes the object invisible; it literally tells your app to render the object as nothing:

    Pass 
    {
      Blend Zero One
    }    // makes this object transparent
  }
}

Great! This process should have looked something like this:

Surface Shader Unity

Open up the surface shader, delete all existing code, and input our new code:

surface shader code

Now we’ll create a new Material, title it “InvisibleMaskMat,” and apply this surface shader to it:

Unity Create New Material

Now whatever object you apply this Material to will become invisible.

Step 2: The Object Blocked In The View

 
As explained above, we have three objects in our example: the X-ray tool, the skin, and the bones. We’ve configured our ‘X-ray tool’ object to be invisible (so we can see objects behind it) and to only show objects that are considered opaque.

At this point, all objects should be seen from behind your invisible object. Next, we’ll add the following script to the object you want to be blocked (and therefore not seen) – in our case, the ‘skin.’

Let’s make a new C# script, title it ‘XRayTool,’ and add some code:

XRay Tool C# Script

The lines after the void Start line identify the object’s mesh render:

	//making sure the object has a renderer
  if(GetComponent())

This next line queues the object’s render order at 3002.

	{
    	GetComponent().material.renderQueue = 3002; //set renderQueue to render after our Invisible mask(3001)
	}
}

Remember that we set opaque objects in the render queue at 2000+, and transparent objects at 3000+? By setting this object to render after opaque objects, we ensure this object’s rendering is blocked. In other words, the skin’s rendering is blocked when the X-ray tool is above it, making it disappear to reveal the ‘bones’ underneath.

Let’s add our XRayTool script to our ‘skin’ (or whatever object you want to turn invisible to see behind) and you are good to go!

XRay Demo in Unity

 

Step 2.5: Does The Object Blocked In The View Have Children?

 
We’ll need to alter our script a bit if your object you want blocked has children associated with it, and you want this blocking effect applied to the children as well. We’ll start off with this:

//If the object in question has children, use following
void Start()
{

Now, to ensure the occlusion effect and delayed rendering applies to the object and its children, include the next few lines:

  //Affects all renderers in this object and its children:
  var renders = GetComponentsInChildren(true);

And then, we’ll delay the object and its children’s point in the rendering queue. In the script we’ve included notes for the other objects’ rendering times:

  foreach (var r in renders)
  {
    r.material.renderQueue = 3002; //sets renderQueue to render after all the transparent objects(3000) including our Invisible Mask(3001)
  }
}

 

Nicely Done!

 
Make sure you apply these two individual scripts to your blocking object (our ‘X-ray tool’) and blocked object (our ‘skin’), and you’re good to go!

You can use the above C# script in any coding program, and for virtual reality or augmented reality. We used it to simulate an X-ray tool, but there are many ways you can use this trick for your own app. Maybe you’ll recreate They Live (1988) in VR!

They Live (1988)

This is just a taste of what you can learn and accomplish in our online VR/AR course. Check out our syllabus to review what we’ll teach you in just 10 weeks with weekly 1:1 sessions.

Download Syllabus

Technical Steps:

 

//How to occlude objects behind transparent objects
 
//Apply following script to invisible object that will make other objects transparent 
Shader "Custom/InvisibleMask"	//make sure you named your shader file InvisibleMask
{
  SubShader
  {
    Tags { "Queue"="Transparent+1" }    // renders after all transparent objects (queue = 3001)
    Pass {  Blend Zero One }    // makes this object transparent
  }
}
 
 
//New script and added to any object to be obscured by invisible mask
void Start()
{
	//making sure the object has a renderer
  if(GetComponent())
	{
    	GetComponent().material.renderQueue = 3002; //set renderQueue to render after our Invisible mask(3001)
	}
}


//If the object in question has children, use following
void Start()
{
  //Affects all renderers in this object and its children:
  var renders = GetComponentsInChildren(true);
  foreach (var r in renders)
  {
    r.material.renderQueue = 3002; //sets renderQueue to render after all the transparent objects(3000) including our Invisble mask(3001)
  }
}