Recently, I read this presentation by Kuba Cupisz and Ole Ciliox and I wanted to try my hand at implementing some of the techniques mentioned in the slides. So far, I’ve written two successful shaders: Rendering the depth buffer and highlighting intersections (the scan field). Since there is no source code to accompany the presentation, I had to do a bit of digging to get everything to work.
The End Result
Here is a quick video of what these two shaders accomplish. Both cubes use the Depth Shader, while the sphere and the plane use the Intersection Highlighting Shader
First Things First
In order to read the depth buffer, we have to tell the camera to write to it. Luckily, that is as easy as flipping a switch:
using UnityEngine;
using System.Collections;
public class TurnOnDepthBuffer : MonoBehaviour {
// Use this for initialization
void Start ()
{
camera.depthTextureMode = DepthTextureMode.Depth;
}
}
Simply put this script on the camera and everything should be good to go. Contrary to the presentation, I tried running rendering in deferred without this script and was unable to get the depth texture.
The Depth Shader
The main reason I wrote this shader is to verify that I was accessing the depth buffer correctly. I doubt it will ever see use in a game, but it serves as a great simplified example.
//Shows the grayscale of the depth from the camera.
Shader "Custom/DepthShader"
{
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _CameraDepthTexture; //the depth texture
struct v2f
{
float4 pos : SV_POSITION;
float4 projPos : TEXCOORD1; //Screen position of pos
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.projPos = ComputeScreenPos(o.pos);
return o;
}
half4 frag(v2f i) : COLOR
{
//Grab the depth value from the depth texture
//Linear01Depth restricts this value to [0, 1]
float depth = Linear01Depth (tex2Dproj(_CameraDepthTexture,
UNITY_PROJ_COORD(i.projPos)).r);
half4 c;
c.r = depth;
c.g = depth;
c.b = depth;
c.a = 1;
return c;
}
ENDCG
}
}
FallBack "VertexLit"
}
The Intersection Highlights Shader
The math behind this shader is covered very well by the presentation, but I’ll write a quick run through. First off, this shader does not write to the depth buffer. This is to prevent it from recording intersections with itself. Instead, it reads the distance from the depth texture to the camera and computes its own. When the two values are reasonably close, that means there is another object at the same distance away from the camera (an intersection). The end result is a neat effect useful for room scans or sci-fi doors.
//Highlights intersections with other objects
Shader "Custom/IntersectionHighlights"
{
Properties
{
_RegularColor("Main Color", Color) = (1, 1, 1, .5) //Color when not intersecting
_HighlightColor("Highlight Color", Color) = (1, 1, 1, .5) //Color when intersecting
_HighlightThresholdMax("Highlight Threshold Max", Float) = 1 //Max difference for intersections
}
SubShader
{
Tags { "Queue" = "Transparent" "RenderType"="Transparent" }
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _CameraDepthTexture; //Depth Texture
uniform float4 _RegularColor;
uniform float4 _HighlightColor;
uniform float _HighlightThresholdMax;
struct v2f
{
float4 pos : SV_POSITION;
float4 projPos : TEXCOORD1; //Screen position of pos
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.projPos = ComputeScreenPos(o.pos);
return o;
}
half4 frag(v2f i) : COLOR
{
float4 finalColor = _RegularColor;
//Get the distance to the camera from the depth buffer for this point
float sceneZ = LinearEyeDepth (tex2Dproj(_CameraDepthTexture,
UNITY_PROJ_COORD(i.projPos)).r);
//Actual distance to the camera
float partZ = i.projPos.z;
//If the two are similar, then there is an object intersecting with our object
float diff = (abs(sceneZ - partZ)) /
_HighlightThresholdMax;
if(diff <= 1)
{
finalColor = lerp(_HighlightColor,
_RegularColor,
float4(diff, diff, diff, diff));
}
half4 c;
c.r = finalColor.r;
c.g = finalColor.g;
c.b = finalColor.b;
c.a = finalColor.a;
return c;
}
ENDCG
}
}
FallBack "VertexLit"
}
Final Thoughts
There were definitely some challenges with getting the shaders to correctly read from the depth texture, but I am really happy with the end results. The intersection highlights is a really good special effect to keep in my back pocket.
Hi, i like this effect, was looking for something like that for quite a while and i have a question: is this ‘pro only’ (is depth Texture a renderTexture?). If yes, could it also be achieved without pro (some other shader trick perhaps) ?
Unfortunately, the Depth Texture is a Render Texture, so it is Unity Pro only. I also do not know of a method for duplicating the effect without using the Depth Buffer.
That’s unfortunate. But thank you for your reply.
Thank you! I really needed good example how to use depth textures in unity, and this one is excellent!