DepthMask

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(Alternate Use)
(Updated to use Material.renderQueue)
Line 4: Line 4:
  
 
[[Image:DepthMaskShader.png|thumb|100px|DepthMask.shader in use on the example project.]]
 
[[Image:DepthMaskShader.png|thumb|100px|DepthMask.shader in use on the example project.]]
This shader draws faces which are invisible, but which still appear in the depth buffer. This makes it possible to prevent subsequently-drawn objects from appearing behind those faces.
+
This shader draws faces which are invisible, but which still appear in the depth buffer. This allows you to prevent objects from being drawn where they are occluded by the mask.
 +
 
 +
To understand how this technique works, you should know about the [http://en.wikipedia.org/wiki/Z-buffering depth buffer] and [http://unity3d.com/support/documentation/Components/SL-SubshaderTags.html render queues] before reading further.
  
 
== Usage ==
 
== Usage ==
  
The example project shows how to use the shader to cut a hole in a water plane to prevent the water from appearing inside a boat's hull.
+
The example package shows how to use the shader to prevent the water from appearing inside a boat's hull. The setup requires two things:
  
Rendering order is handled automatically using Render Queues.
+
# A mask object using the Depth Mask shader. This object will be drawn just after regular opaque objects, and will prevent subsequent objects from being drawn behind it.
 
+
# Objects you wish to be masked must have the SetRenderQueue script attached to them. In the Inspector, change their queue from 3000 (regular geometry) to 3020 (just after the mask shader)
Anything that needs holes cut in it must use a shader that renders after the mask. Examples of how to make the normal diffuse and specular shaders work with masks are given below.
+
  
 
==== Alternate Use ====
 
==== Alternate Use ====
  
If you're trying to create a "hole" or "window" in a scene (e.g. you want the skybox to show through and clip out any geometry behind a rendered object) use the DepthMask shader with Tags {"Queue" = "Geometry-10" } (instead of +10). This essentially turns the shaded object into an "eraser" that knocks the pixels covered by the object out of the render (reseting them to the background).
+
In most common scenarios, you will only need a few objects to be masked. If, however, you find that you have more objects that need to be masked than objects that do not, you might find it useful to attach the SetRenderQueue script to the masks themselves, and set their queues to 2090 (just before regular geometry). Objects that you don't want masked should have their queues set to 2080 (just before the mask shader).
  
== Example Project ==
+
== Example ==
  
Unity 2.1 project: [http://torch.cs.dal.ca/~brauer/Boat%204.zip Boat 4.zip, 532KB]
+
Unity 3.4.1 package: [http://dl.dropbox.com/u/6231276/Depth%20Mask%20Example.unitypackage, 111KB]
  
 
==ShaderLab - DepthMask.shader==
 
==ShaderLab - DepthMask.shader==
Line 27: Line 28:
 
Shader "Masked/Mask" {
 
Shader "Masked/Mask" {
 
 
    SubShader {
+
SubShader {
 
// Render the mask after regular geometry, but before masked geometry and
 
// Render the mask after regular geometry, but before masked geometry and
 
// transparent things.
 
// transparent things.
Line 33: Line 34:
 
Tags {"Queue" = "Geometry+10" }
 
Tags {"Queue" = "Geometry+10" }
 
 
        // Turn off lighting, because it's expensive and the thing is supposed to be
+
// Don't draw in the RGBA channels; just the depth buffer
        // invisible anyway.
+
 
 
        Lighting Off
+
ColorMask 0
 
+
ZWrite On
        // Draw into the depth buffer in the usual way.  This is probably the default,
+
        // but it doesn't hurt to be explicit.
+
// Do nothing specific in the pass:
 
+
        ZTest LEqual
+
Pass {}
        ZWrite On
+
}
 
+
        // Don't draw anything into the RGBA channels. This is an undocumented
+
        // argument to ColorMask which lets us avoid writing to anything except
+
        // the depth buffer.
+
 
+
        ColorMask 0
+
 
+
        // Do nothing specific in the pass:
+
 
+
        Pass {}
+
    }
+
 
}
 
}
 
</shaderlab>
 
</shaderlab>
  
==ShaderLab - MaskedDiffuse.shader==
+
==C# - SetRenderQueue.cs==
  
<shaderlab>
+
<csharp>
Shader "Masked/Diffuse Masked" {
+
/*
Properties {
+
SetRenderQueue.cs
_Color ("Main Color", Color) = (1,1,1,1)
+
_MainTex ("Base (RGB)", 2D) = "white" {}
+
Sets the RenderQueue of an object's materials on Awake. This will instance
}
+
the materials, so the script won't interfere with other renderers that
SubShader {
+
reference the same materials.
// This shader does the same thing as the Diffuse shader, but after masks
+
*/
// and before transparent things
+
 
Tags {"Queue" = "Geometry+20" }
+
using UnityEngine;
UsePass "Diffuse/BASE"
+
UsePass "Diffuse/PPL"
+
}
+
FallBack "Diffuse", 1
+
}
+
</shaderlab>
+
  
==ShaderLab - MaskedSpecular.shader==
+
[AddComponentMenu("Rendering/SetRenderQueue")]
  
<shaderlab>
+
public class SetRenderQueue : MonoBehaviour {
Shader "Masked/Specular Masked" {
+
Properties {
+
[SerializeField]
_Color ("Main Color", Color) = (1,1,1,1)
+
protected int[] m_queues = new int[]{3000};
_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
+
_Shininess ("Shininess", Range (0.01, 1)) = 0.078125
+
protected void Awake() {
_MainTex ("Base (RGB)", 2D) = "white" {}
+
Material[] materials = renderer.materials;
 +
for (int i = 0; i < materials.Length && i < m_queues.Length; ++i) {
 +
materials[i].renderQueue = m_queues[i];
 +
}
 
}
 
}
SubShader {
 
// This shader does the same thing as the Specular shader, but after masks
 
// and before transparent things
 
Tags {"Queue" = "Geometry+20" }
 
UsePass "Specular/BASE"
 
UsePass "Specular/PPL"
 
}
 
FallBack "Diffuse", 1
 
 
}
 
}
</shaderlab>
+
</csharp>

Revision as of 19:16, 25 September 2011

Author: Neil Carter (NCarter) and Daniel Brauer (Danielbrauer)

Contents

Description

DepthMask.shader in use on the example project.

This shader draws faces which are invisible, but which still appear in the depth buffer. This allows you to prevent objects from being drawn where they are occluded by the mask.

To understand how this technique works, you should know about the depth buffer and render queues before reading further.

Usage

The example package shows how to use the shader to prevent the water from appearing inside a boat's hull. The setup requires two things:

  1. A mask object using the Depth Mask shader. This object will be drawn just after regular opaque objects, and will prevent subsequent objects from being drawn behind it.
  2. Objects you wish to be masked must have the SetRenderQueue script attached to them. In the Inspector, change their queue from 3000 (regular geometry) to 3020 (just after the mask shader)

Alternate Use

In most common scenarios, you will only need a few objects to be masked. If, however, you find that you have more objects that need to be masked than objects that do not, you might find it useful to attach the SetRenderQueue script to the masks themselves, and set their queues to 2090 (just before regular geometry). Objects that you don't want masked should have their queues set to 2080 (just before the mask shader).

Example

Unity 3.4.1 package: 111KB

ShaderLab - DepthMask.shader

<shaderlab> Shader "Masked/Mask" {

SubShader { // Render the mask after regular geometry, but before masked geometry and // transparent things.

Tags {"Queue" = "Geometry+10" }

// Don't draw in the RGBA channels; just the depth buffer

ColorMask 0 ZWrite On

// Do nothing specific in the pass:

Pass {} } } </shaderlab>

C# - SetRenderQueue.cs

<csharp> /* SetRenderQueue.cs

Sets the RenderQueue of an object's materials on Awake. This will instance the materials, so the script won't interfere with other renderers that reference the same materials.

  • /

using UnityEngine;

[AddComponentMenu("Rendering/SetRenderQueue")]

public class SetRenderQueue : MonoBehaviour {

[SerializeField] protected int[] m_queues = new int[]{3000};

protected void Awake() { Material[] materials = renderer.materials; for (int i = 0; i < materials.Length && i < m_queues.Length; ++i) { materials[i].renderQueue = m_queues[i]; } } } </csharp>

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox