Search Unity

[Unity 5.6.0f3] empty stencil buffer OnRenderImage

Discussion in 'Image Effects' started by Cyril-Mishakin, May 30, 2017.

  1. Cyril-Mishakin

    Cyril-Mishakin

    Joined:
    Aug 1, 2013
    Posts:
    5
    Hi all

    I have very simple stencil based image effect that perfectly works in Unity 5.5.2 all it does is "bit mask image" output.

    The problem is that this effect in Unity 5.6.0f3 gives solid black screen and it looks like stencil buffer is empty.

    Its very critical for me to fix this problem using 5.6.0f3 version or to find other solution.
    I will be very grateful to any answer.

    Thanks

    Image effect screenshot from unity 5.5.2



    Effect code example
    Code (csharp):
    1.     private void OnRenderImage (RenderTexture source, RenderTexture destination) {
    2.         Graphics.Blit (source, destination, effectMaterial);
    3.     }
    Objects shader example
    Code (csharp):
    1.      SubShader {
    2.          Pass {
    3.              Stencil {
    4.                  Ref 128
    5.                  Pass Replace
    6.              }
    7.  
    8.              CGPROGRAM
    9.              #pragma vertex vert
    10.              #pragma fragment frag
    11.  
    12.              #include "UnityCG.cginc"
    13.  
    14.              float4 vert (float4 vertex : POSITION) : SV_POSITION{
    15.                  return UnityObjectToClipPos(vertex);
    16.              }
    17.  
    18.              fixed4 frag () : SV_Target {  
    19.                  return fixed4(0.5, 0.5, 0.5, 1);
    20.              }
    21.              ENDCG
    22.          }
    23.      }

    Image effect shader example
    Code (csharp):
    1.     SubShader {
    2.          Pass {
    3.              Cull Off ZWrite Off ZTest Always
    4.              Stencil {
    5.                  Ref 128
    6.                  Comp NotEqual
    7.              }
    8.  
    9.              CGPROGRAM
    10.              #pragma vertex vert
    11.              #pragma fragment frag
    12.  
    13.              #include "UnityCG.cginc"
    14.  
    15.              float4 vert (float4 vertex : POSITION) : SV_POSITION{
    16.                  return UnityObjectToClipPos(vertex);
    17.              }
    18.  
    19.              fixed4 frag () : SV_Target {  
    20.                  return fixed4(0, 0, 0, 1);
    21.              }
    22.              ENDCG
    23.          }
    24.  
    25.          Pass {
    26.          Cull Off ZWrite Off ZTest Always
    27.              Stencil {
    28.                  Ref 128
    29.                  Comp Equal
    30.              }
    31.  
    32.              CGPROGRAM
    33.              #pragma vertex vert
    34.              #pragma fragment frag
    35.  
    36.              #include "UnityCG.cginc"
    37.  
    38.              float4 vert (float4 vertex : POSITION) : SV_POSITION{
    39.                  return UnityObjectToClipPos(vertex);
    40.              }
    41.  
    42.              fixed4 frag () : SV_Target {  
    43.                  return fixed4(1, 1, 1, 1);
    44.              }
    45.              ENDCG
    46.          }
    47.      }
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    First of all, I would not recommend just setting the reference value to 128. Instead you should keep the reference value to the default value of 255 and set the ReadMask and WriteMask to 128. This will give you an actual check on a single bit.

    In the current case you are writing and checking 10000000 binary. In the latter case you are writing and checking 1xxxxxxx, where the x's are ignored.

    Unity probably also uses the stencil buffer here and there and you will need to try and coexist with that. So now that you are actually only looking at a single bit instead of all eight, you can try each bit to see whether that works. (1, 2, 4, 8, 16, 32, 64 and 128.)
     
  3. Cyril-Mishakin

    Cyril-Mishakin

    Joined:
    Aug 1, 2013
    Posts:
    5
    Hi jvo3dc, thanks for your reply and recommendations.
    I've already checked each bit and result is the same.

    Today I was able to achieve same result with command buffer in unity 5.6.0f3.
    But currently I'm facing new problem cause this effect is used to detect if object is visible through opaque enviroment by reading pixels to a texture and checking their color. After pixels are read I need to return to image without any effects and I have no idea on how to achieve this kind of functionality using command buffer (I'm new to it).

    So currently I need to understand if its a bug in unity that will be fixed in next versions or they've changed the API and added more steps for this kind of functionality in OnRenderImage or maybe completely removed it.
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It could well be a "bug". On the other hand, it's not really clear which parts of the stencil buffer are used when by Unity itself and which are free for us to use.

    If you can get in between before it is cleared using a CommandBuffer you would have a good workaround, but they indeed require some getting used to.
     
  5. Cyril-Mishakin

    Cyril-Mishakin

    Joined:
    Aug 1, 2013
    Posts:
    5
    Solved problem with CommandBuffer.
    Code (CSharp):
    1.     public Material effectMaterial;
    2.     private CommandBuffer commandBuffer;
    3.  
    4.     private Texture2D m_Texture;
    5.     private Texture2D texture {
    6.         get {
    7.             if (m_Texture == null) {
    8.                 m_Texture = new Texture2D(16, 16);
    9.             }
    10.  
    11.             return m_Texture;
    12.         }
    13.     }
    14.  
    15.     private Camera m_Camera;
    16.     public Camera camera {
    17.         get {
    18.             if (m_Camera == null) {
    19.                 m_Camera = GetComponent<Camera> ();
    20.             }
    21.  
    22.             return m_Camera;
    23.         }
    24.     }
    25.        
    26.     private void Start () {
    27.         if (commandBuffer == null)
    28.         {
    29.             commandBuffer = new CommandBuffer();
    30.             commandBuffer.name = "commandBuffer";
    31.  
    32.             int stencilTextureID = Shader.PropertyToID("_StencilTexture");
    33.             commandBuffer.GetTemporaryRT(stencilTextureID, -1, -1, 24);
    34.  
    35.             commandBuffer.Blit(BuiltinRenderTextureType.None, stencilTextureID, effectMaterial);
    36.  
    37.             commandBuffer.SetGlobalTexture("_StencilTexture", stencilTextureID);
    38.             camera.AddCommandBuffer(CameraEvent.AfterForwardAlpha, commandBuffer);
    39.         }
    40.     }
    41.  
    42.     private void OnRenderImage (RenderTexture source, RenderTexture destination) {
    43.         RenderTexture stencilRT = Shader.GetGlobalTexture ("_StencilTexture") as RenderTexture;
    44.         RenderTexture activeRT = RenderTexture.active;
    45.         RenderTexture.active = stencilRT;
    46.  
    47.         texture.ReadPixels (new Rect (Screen.width / 2, Screen.height / 2, texture.width, texture.height), 0, 0, false);
    48.         //Debug.Log (texture.GetPixel(0, 0));
    49.  
    50.         RenderTexture.active = activeRT;
    51.  
    52.         Graphics.Blit (source, destination);
    53.     }
     
  6. Cyril-Mishakin

    Cyril-Mishakin

    Joined:
    Aug 1, 2013
    Posts:
    5
    Found something interesting. If MSAA is turned ON on the rendering camera you'll get black texture. From my point of view this functionality isn't connected with MSAA. So its very odd. And now I need to solve this some how
     
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Well, MSAA does also need to be applied to the depth/stencil buffer besides the color target. So at 4x MSAA, the depth/stencil buffer should also be able to store 4 samples per pixel.
     
  8. Cyril-Mishakin

    Cyril-Mishakin

    Joined:
    Aug 1, 2013
    Posts:
    5
    MSAA Solution
    Code (CSharp):
    1.     private void OnEnable () {
    2.         if (commandBuffer == null)
    3.         {
    4.             commandBuffer = new CommandBuffer();
    5.             commandBuffer.name = "commandBuffer";
    6.  
    7.             int cachedScreenImageID = Shader.PropertyToID("_Temp");
    8.             commandBuffer.GetTemporaryRT(cachedScreenImageID, -1, -1, 0);
    9.  
    10.             commandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, cachedScreenImageID);
    11.             commandBuffer.Blit(cachedScreenImageID, BuiltinRenderTextureType.CameraTarget, effectMaterial);
    12.        
    13.             commandBuffer.SetGlobalTexture("_CachedScreenImage", cachedScreenImageID);
    14.             camera.AddCommandBuffer(CameraEvent.AfterForwardAlpha, commandBuffer);
    15.         }
    16.     }
    17.  
    18.     private void OnRenderImage(RenderTexture source, RenderTexture destination) {
    19.         RenderTexture cachedScreenRT = Shader.GetGlobalTexture("_CachedScreenImage") as RenderTexture;
    20.  
    21.         texture.ReadPixels(new Rect(Screen.width / 2, Screen.height / 2, texture.width, texture.height), 0, 0, false);
    22.         Debug.Log(texture.GetPixel(0, 0));
    23.  
    24.         Graphics.Blit(cachedScreenRT, destination);
    25.     }
     
    Last edited: Jun 2, 2017
    sewy likes this.
  9. ding0312

    ding0312

    Joined:
    Sep 1, 2015
    Posts:
    3
    Code (CSharp):
    1.  private void OnEnable()
    2.     {
    3.         if (commandBuffer == null)
    4.         {
    5.             commandBuffer = new CommandBuffer();
    6.             commandBuffer.name = "commandBuffer";
    7.             commandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget, postStencilRed);
    8.             camera.AddCommandBuffer(CameraEvent.AfterForwardAlpha, commandBuffer);
    9.         }
    10.     }
     
    Timboc and sewy like this.