Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

SetTargetBuffers does not render UI Canvas

Discussion in 'Image Effects' started by Deleted User, Oct 31, 2017.

  1. Deleted User

    Deleted User

    Guest

    Hey all,

    I'm trying to implement some basic post-processing with multiple render targets with a simple setup like this:

    Code (CSharp):
    1. public class CameraPostProcessing : MonoBehaviour {
    2.  
    3.     public Material postProcessingMaterial;
    4.     private Camera cam;
    5.  
    6.     private RenderTexture mainTex;
    7.     private RenderTexture effectsTex;
    8.  
    9.     void Start () {
    10.         mainTex = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
    11.         effectsTex = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.
    12. ARGBHalf
    13. );
    14.         cam = GetComponent<Camera>();
    15.         RenderBuffer[] rb = new RenderBuffer[] {mainTex.colorBuffer, effectsTex.colorBuffer};
    16.         cam.SetTargetBuffers(rb, mainTex.depthBuffer);
    17.         postProcessingMaterial.SetTexture("_EffectsTex", effectsTex);
    18.     }
    19.  
    20.     void OnRenderImage(RenderTexture src, RenderTexture dest) {
    21.         Graphics.Blit(mainTex, dest, postProcessingMaterial);
    22.     }
    23.  
    24. }
    It works great except one little detail: UI is not rendered at all. I assumed it has something to do with me discarding the `src` in `OnRenderImage`, but it appears to be not the case: this texture is pure white.

    I'm using Unity 5 standard UI Canvas with Render Mode "Screen Space — Overlay".

    Can't really figure it out, any help is much appreciated!
     
  2. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    Can you post a screenshot of the whole (or at least the last part) of the Frame Debugger contents?
    Most likely something in your scene gets rendered after the Canvas.RenderOverlays (I don't remember the exact call)
     
    Deleted User likes this.
  3. Deleted User

    Deleted User

    Guest

    Firstly, thank you for introducing me to Frame Debugger :D (I'm sorry, I'm such a newbie).

    Secondly, I don't see anything happening after Canvas.RenderOverlays:

    Screen Shot 2017-10-31 at 22.25.32.png
     
  4. Deleted User

    Deleted User

    Guest

    Once thing I noticed is that if I remove the SetTargetBuffers call (which will lead to UI rendering correctly and post-processing not rendering at all), then RenderTarget in Frame Debugger shows <No name> instead of an empty string. With SetTargetBuffers call enabled, Frame Debugger doesn't display a "preview" in Game tab when going through draw calls, and RenderTarget is an empty string, just exactly as shown in a screenshot above.

    I wonder if I need to return something to original state in OnPostRender call... I already tried memorizing Graphics.activeColorBuffer and Graphics.activeDepthBuffer in OnPreRender and setting them back in OnPostRender — but then buffers are not rendered at all :confused:
     
    Last edited by a moderator: Oct 31, 2017
  5. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    What happens when you click those separate draw calls? Does anything get rendered in the game view?
     
  6. Deleted User

    Deleted User

    Guest

    Nope, with SetTargetBuffers enabled game view doesn't update at all.
     
  7. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    Is it possible to receive your project example to investigate?
     
  8. Deleted User

    Deleted User

    Guest

    Sure, here's a demo project which illustrates the issue.

    In short: Object shader writes mask to COLOR1 buffer, which is subsequently captured by SetTargetBuffers and passed to PostProcessing shader to draw an outline.

    If you comment out SetTargetBuffers call and OnRenderImage, then you will be able to see the UI — but no outlines.
     

    Attached Files:

  9. Deleted User

    Deleted User

    Guest

    Some further investigation shows that calling SetTargetBuffers affects source and destination parameters in OnRenderImage. Src unsurprisingly becomes Null, but what's rather unexpected is that Dest becomes "ImageEffects Temp Buffer" instead of Null.

    I tried manipulating RenderTexture.active and all sorts of Graphics.activeColorBuffer/activeDepthBuffer juggling, but this either gives no effect, or a black screen.
     
  10. Deleted User

    Deleted User

    Guest

    Ok, the SetTargetBuffers is a big box of surprises.

    Long story short, with (incorrect) code like this:

    Code (CSharp):
    1.     public Material postProcessingMaterial;
    2.     private Camera cam;
    3.  
    4.     private RenderTexture mainTex;
    5.     private RenderTexture effectsTex;
    6.  
    7.     void Start () {
    8.         cam = GetComponent<Camera>();
    9.     }
    10.  
    11.     void OnPreRender() {
    12.         mainTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
    13.         effectsTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBHalf);
    14.         RenderBuffer[] rb = new RenderBuffer[] {mainTex.colorBuffer, effectsTex.colorBuffer};
    15.         postProcessingMaterial.SetTexture("_EffectsTex", effectsTex);
    16.         cam.SetTargetBuffers(rb, mainTex.depthBuffer);
    17.     }
    18.  
    19.     void OnRenderImage(RenderTexture src, RenderTexture dst) {
    20.         Graphics.Blit(mainTex, dst, postProcessingMaterial);
    21.         /*
    22.         RenderTexture.ReleaseTemporary(mainTex);
    23.         RenderTexture.ReleaseTemporary(effectsTex);
    24.         */
    25.         mainTex.Release();
    26.         effectsTex.Release();
    27.     }
    — I get exactly the results I want (that is, both effects and UI are rendered correctly). With one "small" caveat: this code is incorrect and spits out exceptions "Releasing render texture whose render buffer is set as Camera's target buffer with Camera.SetTargetBuffers!" on every frame.

    However, if I replace Release calls with RenderTexture.ReleaseTemporary, then no exceptions are thrown. And no UI rendered either :mad:

    Unless there's something I'm missing out, I really assume this is a bug. Can some one confirm/infirm please?
     
  11. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    I have just opened your example but can't see any outline effect.
    upload_2017-11-3_0-29-9.png
    I also can't see UI, which is, as you've said, your problem. Commenting out the command buffers line just renders everything black. But I won't be able to see if any of my actions have fixed it or not because I won't be able to tell if the effect works or not :) Can you provide some additional info on this? I'm using 2017.2, just like you (I've checked the project settings)
     
  12. Deleted User

    Deleted User

    Guest

    @Kumo-Kairo Thanks for looking into it! And sorry to hear that it doesn't work for you, I assume I'm using something OpenGL-specific which probably doesn't work on DirectX (I have no Windows to test).

    Anyway, I kinda gave up on the idea of using SetTargetBuffers on main camera. Initially it seemed like a win, because all geometry is rendered exactly once, and we can encode lots of usefulness into a separate buffer to use at later post-processing stages. But in practice setting SetTargetBuffers has unwanted side-effects (like buffer[0] still being set as render target even if you reset it in OnPostRender) — and this basically prevents UI from being rendered to screen.

    I will now try using stencils (I think it's possible to write some bits to Z buffer from fragment shader and use it later in post-processing). Will post the results here.

    Thanks again for your help!
     
    wuxingogo likes this.
  13. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    Can you show a screenshot of an effect you're trying to achieve?
     
  14. Deleted User

    Deleted User

    Guest

    Yes, sure. Finally managed to achieve what I was going after:

    outline.gif

    So I still used SetTargetBuffers — but this time on a separate camera, leaving main camera with cullingMask = 0 (render nothing), and then blitting multiple buffers together with a post processing shader. Initially I was reluctant to introduce another camera, because it "almost worked" with the main one. But I figured that SetTargetBuffers just isn't designed to work with actively rendering camera (UI being rendered into wrong buffer is just one of the consequences).

    Actually, much to my surprise, additional camera setup adds almost 0 complexity: all we have to do is to create an empty game object inside main camera, attach a Camera component to it, then in controller do cam.CopyFrom(Camera.main) — or even initialize some settings manually in Editor. Finally, the code isn't too different from the one I posted in the beginning:

    Code (CSharp):
    1. public class CameraCustomRenderer : MonoBehaviour {
    2.  
    3.     public Camera mrtCamera;
    4.     public Material postProcessingMaterial;
    5.  
    6.     private RenderTexture mainTex;
    7.     private RenderTexture effectsTex;
    8.  
    9.     void Start () {
    10.         mainTex = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
    11.         effectsTex = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBHalf);
    12.         RenderBuffer[] rb = new RenderBuffer[] { mainTex.colorBuffer, effectsTex.colorBuffer };
    13.         postProcessingMaterial.SetTexture("_EffectsTex", effectsTex);
    14.         mrtCamera.SetTargetBuffers(rb, mainTex.depthBuffer);
    15.         mrtCamera.enabled = false;
    16.     }
    17.  
    18.     void OnRenderImage(RenderTexture src, RenderTexture dest) {
    19.         mrtCamera.Render();
    20.         Graphics.Blit(mainTex, dest, postProcessingMaterial);
    21.         Graphics.SetRenderTarget(effectsTex);
    22.         GL.Clear(false, true, Color.black);
    23.     }
    24.  
    25. }
    Few more things:

    As I mentioned, my main camera renders nothing at all, not even UI (culling mask is 0, or "Nothing"). This is the trick I learned from my adventures: if Canvas is set to its default rendering mode "Screen Space — Overlay", then it gets rendered regardless of what you specify in camera's culling. And, ironically, this is the very reason I started this thread :D

    Also, if you have Physics Raycaster on your main camera (I happened to have one), just move it to the additional camera which actually does the rendering. Apparently, culling mask is also applied to raycasters (and maybe other components as well, for optimization).
     
    Chaosed0, wxxhrt, KUDr and 1 other person like this.