Sure:
Shader "Custom/CensorFX"
{
Properties
{
_RenderedScene("RenderedScene", 2D) = "white" {} //Should contain render of scene so far
}
SubShader
{
Pass
{
ZWrite Off
ZTest Always
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _RenderedScene;
float4 _RenderedScene_TexelSize;
struct VertexInput
{
float4 modelPos : POSITION;
};
struct PixelInput
{
float4 ndcPos : POSITION;
float4 screenPos : TEXCOORD0; //same as ndcPos. Have to pass as separate variable to use in pixel shader since ndcPos is "consumed" by the pipeline
//have let GPU interpolate the screen position, then calculate UVs in fragment shader to have mesh appear completely flat
};
PixelInput vert(VertexInput vertexInput)
{
PixelInput output;
output.ndcPos = mul(UNITY_MATRIX_MVP, vertexInput.modelPos);
output.screenPos = output.ndcPos;
return output;
}
float4 frag(PixelInput pixelInput) : COLOR
{
//calculate the uv's based on the screen space position so
//that top left corner in screen space samples from top left
//of texture, bottom right corner in screen space samples
//from bottom right of texture, etc...
//
//if the _RenderedScene texture is really small and point filtered,
//this mesh will basically pixelate a certain portion of the screen.
float2 uv = (pixelInput.screenPos.xy / pixelInput.screenPos.w + 1) / 2;
#if UNITY_UV_STARTS_AT_TOP
if (_RenderedScene_TexelSize.y < 0) //this is returning false even when the image is upside down!
{
uv.y = 1 - uv.y;
}
#endif
return tex2D(_RenderedScene, uv);
}
ENDCG
}
}
FallBack "Diffuse"
}
To give you a little more context, this is used for a censorship effect to basically pixelate certain portions of the final image. I will also post the OnRenderImage function I am using in the image effect:
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (_censorMeshesToDraw.Count > 0)
{
RenderTexture downSampledScreenCap = GetTempRenderTexture(); //scaled down by 8 times and point filtered
//down sample a smaller version to pass to shader
//(If I were to save 'downSampledScreenCap' as a png now,
//it would be right side up)
Graphics.Blit(source, downSampledScreenCap);
//pass the full image through the pipeline
//(If I were to save 'destination' as a png now,
//it would be right side up).
Graphics.Blit(source, destination);
//pass the scaled down image to the shader, and render the
//censor meshes with this material.
_censorMaterial.SetTexture(RENDERERD_SCENE_PROPERTY_NAME, downSampledScreenCap);
_censorMaterial.SetPass(0);
foreach (CensorMesh censorMesh in _censorMeshesToDraw)
{
Graphics.DrawMeshNow(censorMesh.Mesh, censorMesh.Transform.localToWorldMatrix);
}
//If I were to save 'destination' as a png now the original part
//of the image (the image before censor meshes were drawn) would
//right side up, but the texture used for the censorship meshes
//('downSampledScreenCap'), is upside down.
RenderTexture.ReleaseTemporary(downSampledScreenCap);
}
else
{
Graphics.Blit(source, destination); //pass the full image through the pipeline
}
}
I think the comments do a pretty good job of explaining how the algorithm works, but let me know if there is anything in the code that you would like me to elaborate on.
Here is some additional information I have found.
-
My specific problem has nothing to do with antialiasing, like the problem in that article; my problem persists whether or not I have antialiasing on.
-
The problem only occurs if there is at least one additional image effect after the censorship effect. If there are no additional image effects after this one, everything works fine.
Given observation number 2, I have found a “hack” around the issue. In the OnRenderImage function, it seems that the destination RenderTexture is null if and only if there are no image effects after the current one. So I can test if the destination is not null, and then when I blit the down sampled image, I can flip it with a simple shader on DirectX.
I have tested my “hack” and it seems to work in all cases. But I still don’t feel very satisfied with the solution. There are three things I do not understand:
-
How is this image is getting flipped in the first place (when I print it out, it is always right side up until the last step)?
-
Why does the problem only happen when there is an additional image effects after the censorship effect?
-
Why does Unity’s proposed solution not work in my case?