Stencil test, comes after the "Blending" in rendering pipeline. What happens here? Here we set a stencil (stencil buffer) for the screen and this stencil is set before drawing any object, so that we can test that object before drawing, against the stencil. This is used to mask some pixels.
This might sound confusing but let me explain that with an example. In the below video as you can see, when i scan body with x-ray, i could see his bones. How does that happen?
Here i have 3 objects:
1. Skeleton structure(Bones)
2. Main Body
3. X-Ray
1. Skeleton structure doesn't need to have any stencil test. So we leave that.
2. Now the X-Ray should have a shader attached, where we write to the stencil buffer. Here we write what pixels we want to mask for the user. As entire x-ray masks, so we set stencil buffer for every x-ray screen pixel to 1.
3. Main Body refers this stencil buffer set by x-ray and hides that pixels in that "x-ray area".
And that is how you see the below effect.
So to summarize, The stencil buffer is a pixel mask that allows you further control of what pixels make it from the scene into the frame buffer(screen).
All stencil operations are done via a small stencil code block.
Subshader
{
Stencil
{
//Stencil Operation
}
}
Stencil have 3 main parameters:
1. Ref: Reference value we operate on with default value 0
2.Comp: Which defines when the stencil operation passes. When this is true: corresponding pixel is displayed on to the screen. when this is false: Corresponding pixel is not displayed on to the screen.
3. Pass: Here in this step, the actual filling of the stencil buffer happens( when comp returns true)
For more information on these, refer to Unity Documentation -
Now that we have x-ray- attach the following code for the x-ray which is actually a quad(with transparent texture)
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "ShaderLearning/StencilBuffer/Xray"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
_Texture("Basic Texture", 2D) = "white" {}
}
Subshader
{
Pass
{
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
Stencil
{
Ref 1
Comp always
Pass replace // This step actually fills the stencil buffer.
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform half4 _Color;
uniform sampler2D _Texture;
uniform float4 _Texture_ST;
struct vertexInput
{
float4 vertex: POSITION;
float4 texcoorda: TEXCOORD0;
};
struct vertexOutput
{
float4 pos : SV_POSITION;
float4 texcoorda: TEXCOORD0;
};
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoorda.xy = v.texcoorda + _Texture_ST.wz;
return o;
}
half4 frag(vertexOutput i) : COLOR
{
return tex2D(_Texture,i.texcoorda) * _Color.rgba;
}
ENDCG
}
}
}
So in the above code all the pixels in the 'x-ray region' are filled with '1' value in stencil buffer.
Stencil
{
Ref 1
Comp always
Pass replace // This step actually fills the stencil buffer with 1 as default values in stencil buffer are 0
}
Now for the Human Body- Attach the following code-
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "ShaderLearning/StencilBuffer/OuterCube"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
}
Subshader
{
Pass
{
// Blend SrcAlpha OneMinusSrcAlpha
ZWrite on
Stencil
{
Ref 1
Comp notequal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform half4 _Color;
struct vertexInput
{
float4 vertex: POSITION;
};
struct vertexOutput
{
float4 pos : SV_POSITION;
};
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(vertexOutput i) : COLOR
{
return _Color;
}
ENDCG
}
}
}
So for the human body - where ever the stencil buffer is not equal to one, that pixel passes the stencil test and is displayed on to the screen. But if the stencil buffer value is equal to 1, that pixel fails the stencil test and is not displayed on to the screen.
Comments