Search Unity

CustomRenderTexture initialization in same update cancels out shader?

Discussion in 'General Graphics' started by Deleted User, Oct 16, 2017.

  1. Deleted User

    Deleted User

    Guest

    I'm trying to use a CustomRenderTexture to achieve an image effects. I have a very simple CustomRenderTexture in my assets folder which has initialization set to texture and set to update in Realtime. Then I have my effect material with my update shader and my named update pass which is also set to update in Realtime.

    What I expect is that at the beginning of every frame the initializer will go first, filling the CustomRenderTexture with the texture and THEN my image effect material/shader will update but it appears that in ANY frame where the initializer executes then the shader material will not be updated/rendered in that frame. So the result is that every frame I'm just seeing the plain, unmodified texture.

    Here is my image effect shader:

    Code (CSharp):
    1. Shader "CustomRenderTexture/MultiplyRed"
    2. {
    3.     Properties
    4.     {
    5.                                 _Tex("InputTex", 2D) = "white" {}
    6.      }
    7.  
    8.      SubShader
    9.      {
    10.         Lighting Off
    11.         Blend One Zero
    12.  
    13.         Pass
    14.         {
    15.             CGPROGRAM
    16.             #include "UnityCustomRenderTexture.cginc"
    17.             #pragma vertex CustomRenderTextureVertexShader
    18.             #pragma fragment frag
    19.              #pragma target 3.0
    20.  
    21.  
    22.                                         sampler2D   _Tex;
    23.  
    24.             float4 frag(v2f_customrendertexture IN) : COLOR
    25.             {
    26.                 return float4(1,0,0,1) * tex2D(_SelfTexture2D, IN.localTexcoord.xy);
    27.             }
    28.             ENDCG
    29.                     }
    30.     }
    31. }
    Note that I do have "Double Buffered" enabled, so I am confused as to why this isn't working! Any advice? Why would you ever want the initializer in Realtime if it means that it can never be updated?
     
  2. Deleted User

    Deleted User

    Guest

    Could maybe a Unity developer explain this behavior?
     
  3. Julien_Unity

    Julien_Unity

    Unity Technologies

    Joined:
    Nov 17, 2015
    Posts:
    72
    Hi.
    The problem in your case is that the Initialization mode is set to realtime.
    Here is how things happen when we update Custom Render Textures:
    - Initialize (if needed)
    - Swap
    - Update

    This is done this way so that when Double Buffer is enabled, you always get at least the initialized result in _SelfTextureXXX (otherwise first frame will always be wrong).

    The subtlety here is that the version of the Custom Texture used when set as a parameter in a Material is not the one currently being rendered to (this is due to underlying technical reasons) but the result of the last swap. So by setting InitMode to Realtime what happens is that it's the reinitialized texture that we use in the material every frame.
    That being said, using double buffered Custom Render Texture with Realtime InitMode is kind of pointless because it means that you actually don't need the result of the previous update in this case but only that of the initialization phase. And this can be achieved completely in a single non-double buffered update.
    In general be aware that unfortunately (for the same technical reasons mentioned above) that using double buffer has a performance hit on top of the memory hit because it involves a copy at each update. So any effect that can be achieved with normal blending modes (ie, any effect that can combine the last update result with the current one with hardware blend functions) should be done this way instead of double buffer.

    Hope this clear things up.

    Cheers
     
    Last edited: Oct 19, 2017
  4. Deleted User

    Deleted User

    Guest

    @Julien_Unity Thank you for this explanation. So if I want to use a single non-double buffered update how do I sample the initialization result texture in the update material shader? Before I was trying to use _SelfTexture2D and double-buffer because I couldn't figure out how to sample the initalization result texture from the documentation and I assumed that the initialized texture would be in the buffer in the first update but that was not true.
     
  5. Julien_Unity

    Julien_Unity

    Unity Technologies

    Joined:
    Nov 17, 2015
    Posts:
    72
    The answer is a bit dependent on what you really want to do.
    If what you are trying to achieve is not dependent on a previous update then the initialization texture can just be another texture in your update material and you combine it with whatever you need in the shader.
    If you want to accumulate update result over time but need a specific initialization state, you can then setup your initialization texture as OnLoad and then you're update shader can use any suitable blend mode to combine the result with the initialization.
    Remember that the initialization step is optional. Setting the Init Mode to "OnDemand" will never trigger it unless explicit in the script. Basically, the only cases where you really need to have an Initialization step is if you are using double buffering or a non-opaque update shader. Otherwise everything can be done in the update shader.
     
  6. Deleted User

    Deleted User

    Guest

    @Julien_Unity What I'm actually trying to do is get around a limitation I hit with using CommandBuffers for a screen-space image effect. What I was doing required a buffer (via Blit to a temporary RenderTexture) because I need to capture the screen contents, blit to a temporary RenderTexture using the image effect shader, then use the results of that image effect shader as an input for another pass.

    Unfortunately I need the timing of the screen capture to happen right before the render queue position of the mesh my shader is applied to but Camera.AddCommandBuffer() needs a CameraEvent and CameraEvents are not synchronized with the shader render queue nor is there enough granularity (I need to render between 3000-3500 in the shader queue but CameraEvent only gives me BeforeForwardAlpha and AfterForwardAlpha)

    So afterall I guess what I need out of CustomRenderTextures was the chaining behavior of being able to chain one into another. My idea was to put a GrabPass in the first CRT, do the first update pass, and hand the result to the next CRT, effectively giving me GrabPass AND a frame buffer. But now that I think about it the timing of the GrabPass won't work out unless the evaluation of the CRT chain STARTS when a material uses the final CRT's output as an input texture or if it happens before any meshes are rendered.
     
  7. Deleted User

    Deleted User

    Guest

    Followed your advice of just putting the GrabPass in the update shader of my CustomRenderTexture update material. Unfortunately this too crashes Unity. :(
     
  8. Julien_Unity

    Julien_Unity

    Unity Technologies

    Joined:
    Nov 17, 2015
    Posts:
    72
    Unfortunately, by design, all updates of Custom Render Textures happen at the beginning of the frame (to make sure everything is ready before any material needs a result and to handle potential dependencies between CRTs), this means that it cannot really access results of grab pass because the rendering has not happened yet (it obviously should not crash though). The only workaround I can see for now is to make a copy of whatever grab pass you need during the frame and then use it during next frame but by the sound of what you are trying to do it might not be acceptable though...
     
  9. Deleted User

    Deleted User

    Guest

    That might be my only option though. How would I go about doing this?
     
  10. Julien_Unity

    Julien_Unity

    Unity Technologies

    Joined:
    Nov 17, 2015
    Posts:
    72
    Not completely familiar on what's possible with grab passes and command buffer (haven't really used them before...) but I think it should be possible to blit from a command buffer inside a RenderTexture created from your side that you keep around and then use it as an input to a CustomRenderTexture material next frame. Someone else more familiar with them might be able to help better :/
     
  11. Deleted User

    Deleted User

    Guest

    I see where you're going but actually now that I think about it that wouldn't solve my problem because of the same reason I tried CRT in the first place: CommandBuffer execution timing doesn't have enough granularity for what I'm trying to do. :/

    I think the only thing that might help is Scriptable Render Loop but that's a ways off and until then I can't release my Asset on the store. :/