- Home /
Manual Graphics.Blit
I'm having some issues preserving stencil buffer data with Graphics.Blit.
In the Unity documentation, it says if you want to preserve stencil buffer data:
Source: https://docs.unity3d.com/ScriptReference/Graphics.Blit.html"Note that if you want to use depth or stencil buffer that is part of the source (Render)texture, you'll have to do equivalent of Blit functionality manually - i.e. Graphics.SetRenderTarget with destination color buffer and source depth buffer, setup orthographic projection (GL.LoadOrtho), setup material pass (Material.SetPass) and draw a quad (GL.Begin)."
Could anyone provide example code for how to do this? It's not listed in the documentation.
I figured out how to do the second part, (and can confirm this preserves stencil buffer data). But haven't used render targets before and documentation is sparse.
Here's what I have so far:
public Shader shader; private Material mat; // Will be called from camera after regular rendering is done. public void OnPostRender() { if (!mat) { mat = new Material(shader); mat.hideFlags = HideFlags.HideAndDontSave; } GL.PushMatrix(); GL.LoadOrtho(); // activate the first shader pass (in this case we know it is the only pass) mat.SetPass(0); // draw a quad over whole screen GL.Begin(GL.QUADS); GL.Vertex3(0, 0, 0); GL.Vertex3(1, 0, 0); GL.Vertex3(1, 1, 0); GL.Vertex3(0, 1, 0); GL.End(); GL.PopMatrix(); }I essentially want to create an image effect, but have to do it manually because of the stencil buffer issue. I want to preserve the camera's texture data (presumably in a buffer, as described) and feed it into the shader as I would in a normal blit operation.
Thanks!
Answer by markefus · Jan 27, 2019 at 06:16 AM
For any lost soul struggling with preserving stencil buffer data in image effects.... It just works.
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);
}
Answer by ArminRigo · Feb 10, 2020 at 10:12 AM
FWIW, I'm trying to use a similar CustomBlit() function and eventually found out the correct tweak. For the next person's usage, I'm copying here the complete thing:
/* Sometimes you need to save and restore the active rendertexture. */
var original = RenderTexture.active;
RenderTexture.active = destination; /* or Graphics.SetRenderTarget(..) */
// Set the '_MainTex' variable to the texture given by 'source'
mat.SetTexture("_MainTex", source);
GL.PushMatrix();
GL.LoadOrtho();
// activate the first shader pass (in this case we know it is the only pass)
mat.SetPass(0);
// draw a quad over whole screen
GL.Begin(GL.QUADS);
GL.TexCoord2(0f, 0f); GL.Vertex3(0f, 0f, 0f); /* note the order! */
GL.TexCoord2(0f, 1f); GL.Vertex3(0f, 1f, 0f); /* also, we need TexCoord2 */
GL.TexCoord2(1f, 1f); GL.Vertex3(1f, 1f, 0f);
GL.TexCoord2(1f, 0f); GL.Vertex3(1f, 0f, 0f);
GL.End();
GL.PopMatrix();
RenderTexture.active = original; /* restore */
The shader itself:
Shader "Unlit/MyCustomBlit"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler _MainTex;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv; /* just copy the coordinates here */
return o;
}
float4 frag (v2f i) : SV_Target
{
float4 input_col = tex2D(_MainTex, i.uv);
/* post-processing here */
return input_col;
}
ENDCG
}
}
}
Answer by markefus · Jan 27, 2019 at 02:21 AM
So have some updates, but it still isn't working. Any ideas??
public Shader shader;
private Material mat;
private Camera cam;
void OnEnable()
{
// Create a material that uses the desired shader
mat = new Material(shader);
// Get the camera object (this script must be assigned to a camera)
cam = GetComponent<Camera>();
}
[ImageEffectOpaque] // Draw after opaque, but before transparent geometry
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// Call custom Blit function
// (usually Graphics.Blit is used)
CustomBlit(source, destination, mat);
}
void CustomBlit(RenderTexture source, RenderTexture destination, Material mat)
{
// Custom Blit
// ===========
// Set the given destination texture as the active render texture
Graphics.SetRenderTarget(destination.colorBuffer, source.depthBuffer);
// Set the '_MainTex' variable to the texture given by 'source'
mat.SetTexture("_MainTex", source);
GL.PushMatrix();
GL.LoadOrtho();
// activate the first shader pass (in this case we know it is the only pass)
mat.SetPass(0);
// draw a quad over whole screen
GL.Begin(GL.QUADS);
GL.Vertex3(0, 0, 0);
GL.Vertex3(1, 0, 0);
GL.Vertex3(1, 1, 0);
GL.Vertex3(0, 1, 0);
GL.End();
GL.PopMatrix();
}