- Home /
Using the stencil-buffer in a post-process?
Hi all,
(Cross-post from the forum - didn't get any response there!)
I'm trying to do a graphical effect where a couple of objects in the scene write to the stencil buffer, and then a post-fx reads the stencil buffer to do a full-screen effect.
The part where the objects write to the stencil buffer is definitely working.
Let's say I just want to write green pixels where the stencil buffer has been written to earlier. Here's my shader:
Shader "Green" { SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry+1"} Pass { Stencil { Ref 1 Comp equal Pass keep ZFail keep }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : COLOR {
return half4(0,1,0,1);
}
ENDCG
}
}
}
If I have my OnRenderImage function blitting straight from source to dest using this shader, everything works as expected - I get solid green on screen only where I wrote to the stencil:
void OnRenderImage (RenderTexture source, RenderTexture dest)
{
Graphics.Blit (source, dest, greenMat);
}
But, if I try to use an intermediate buffer, it seems to ignore the Stencil section of the pass, and just write green to every pixel:
void OnRenderImage (RenderTexture source, RenderTexture dest)
{
RenderTexture buffer = RenderTexture.GetTemporary(source.width, source.height, 0);
Graphics.Blit (source, buffer, greenMat);
Graphics.Blit(buffer, dest);
}
This just results in a completely green screen.
I've noticed that there's a RenderTexture.SupportsStencil method, which returns false for my buffer, but I can't find any documentation on why, or how to change it. Is that even relevant? I only want to use the main screen's stencil buffer.
This is on PC.
Any help would be much appreciated, Rob.
Answer by markefus · Sep 15, 2016 at 12:44 PM
Even though this question is two and a half years old, there is nowhere on the internet with the correct answer. I spent a long time figuring it out.
This should allow you to use the stencil buffer in a post-process effect.
Thanks for your example But I find out it doesn't work for the first time. It turns out we don't need the "buffer" parameter for the first Blit. Graphics.Blit(source,material);
It's working for me now.
Answer by xjqxzyr · May 16, 2017 at 04:18 PM
Recently,I make a post effect of sss by blur the character skin in post effect,but need stencil buffer,I did many way tries,The actual truth is that antialiasing or other post effect before mine cleared the stencil buffer ,if you want to get the correct stencil buffer you could disable AA or other post effects. Though I found that the cam.clearStencilAfterLightingPass is always false,and use the [ImageEffectOpaque] attribute did help nothing. By the way , I have to tell the truth ,The qiankanglai did the right ways.
Answer by qklxtlx · Mar 07, 2015 at 08:15 AM
It seems stencil is cleared before OnRenderImage. I came up with a method to use this for post process:
Draw in
OnPostRender
Blit
with the origin depth buffer & new color buffer
I think you should try only drawing the buffer
color buffer + dest
depth buffer.
ps. I've posted more details and a fastBloom with stencil buffer in my blog
Answer by Sparrowfc · Mar 31, 2015 at 09:13 AM
Thank for qklxtlx's reply, which enlightens me a lot. The key point is to use same stencil buffer when using Graphic.Blit. For your case, just replace line 5 with code below:`
Graphics.SetRenderTarget(buffer.colorBuffer, source.depthBuffer);
Graphics.Blit(source, material);
To be more sepecific, when you use RenderTarget.GetTempory(int width, int height) to get a RenderTarget, that target don't contain the stencil buffer, becouse the depth bit is set to 0 in default. Use RenderTarget.GetTempory(int width, int height, 24) to get a target with depth/stencil buffer. (It's like create depth buffer using D24S8 formate in D3D I guess)
Answer by markefus · Jan 27, 2019 at 06:00 AM
public Shader shader;
private Material mat;
void OnEnable()
{
mat = new Material(shader);
}
//'It Just Works' -- Todd Howard
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
RenderTexture buffer = RenderTexture.GetTemporary(source.width, source.height, 24);
Graphics.Blit(source, buffer, mat);
Graphics.Blit(buffer, destination);
RenderTexture.ReleaseTemporary(buffer);
}
This doesn't work for me.
This is the shader code I use:
Shader "Hidden/OnionSkinning"
{
Properties
{
_$$anonymous$$ainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Cull Off ZWrite Off ZTest Always
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
CGINCLUDE
struct Input
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
Varyings Vertex(Input v)
{
Varyings o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _$$anonymous$$ainTex;
ENDCG
Pass
{
Stencil {
Ref 1
Comp equal
Pass keep
ZFail keep
}
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(1,1,1,1);
return col;
}
ENDCG
}
}
}
Your answer

Follow this Question
Related Questions
Is it possible to AlphaTest prior to writing to the Stencil Buffer? 1 Answer
Shadows on terrain splat map shader 0 Answers
How do I use custom tree geometry in Unity? 2 Answers
Missing shaders when import from Maya 0 Answers
About the shader of UIToolkit 0 Answers