Search Unity

SRP and grabpass with transparent.

Discussion in 'Shaders' started by kripto289, Sep 15, 2018.

  1. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    Hello,
    Sorry for bad english.

    Unity removed grabpass from new SRP rendering and added new useless for some cases "_OpaqueRenderTexture".
    Right now impossible to do a heat distortion for fire/particles/etc, or a water/underwater/glass effect which can't see a transparent objects and particles.

    I can grab image after transparent (with comand buffer), but then I'll grab the transparent image recursively.
    I've tried add to command buffer shader keywords of shader global float for avoid grab recursively.
    But it doesnot work, because I can't change render queue of buffer bliting (for example transparent 3000 -> blit 3001 -> head distortion 3002).


    I can use new camera which will render scene again and use somethink like that

    Shader.EnableKeyword("Aboid_Grab");
    camera.renderTexture = GrabTexture;
    camera.Render();
    Shader.DisableKeyword("Aboid_Grab");
    Shader.SetGlobalTexture("GrabTexture", GrabTexture);
    /// shader code
    void frag(...)
    {
    #ifdef Aboid_Grab
    discard; // or return 0;
    #endif

    //some distortion code
    }
    But render scene twice it's bad.

    I can (for some effects) render only normals and use it for heat distortion as posteffect, but what I should do for other effects, like a water, decals etc?

    In the new rendering, is it not possible to do some of the effects that are available in the old version?
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Can't you just add a CommandBuffer after the transparent parts are rendered and then in it:
    - Make a copy
    - Apply the distortion effect

    Why is that?
     
  3. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    In the scene I have for example transparent quad with distortion shader.
    I press play
    First frame: I don't have "grabTexture" and transparent quad have black color.
    Command buffer grab the scene (with this transparent black quad).
    Second frame:
    Render transparent quad with "grabTexture" from previous scene.
    Right now I have quad with black color (but from previous image).
    etc
    It's like that


    Alas, command buffer can't ignore this transparent quad :(
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    So you mean recursively frame by frame. Well, yes, what else did you expect? If you want to ignore a part, you'll have to do that yourself. For example by marking the stencil buffer.
     
  5. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,982
    Could you describe for a layman how this would be done? Im not looking for snippets or anything, just want to know the steps so I can research this in my own time
     
  6. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    Stencil buffer will always have the same value for current view image and for command buffer blit pass.
     
  7. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    Hey @kripto289! I'm also struggling with the lack of grabpass support on SRP and can't find any solution on the web. Have you managed to solve this issue in any way?
     
  8. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    505
    Yes I found the solution for LWRP and HDRP (it's different method).
    For lwrp you can render custom shader with custom render queue.
    opaque order -> transparent order -> custom distortion shader

    1) You need add this line for your distortion shader
    Code (CSharp):
    1. " Tags { ....  "LightMode" = "CustomDistortion" }
    2) Use "sampler2D _GrabTexture"

    3) Add this script to camera (tested on unity 2018.3 and with new lwrp it need some fixes)

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Experimental.Rendering;
    3. using UnityEngine.Experimental.Rendering.LightweightPipeline;
    4. using UnityEngine.Rendering;
    5.  
    6.  
    7. //[RequireComponent(typeof(PostProcessLayer))]
    8. public class RFX1_LWRP_RenderDistortion: MonoBehaviour, IAfterTransparentPass
    9. {
    10.     public bool IsActive = true;
    11.     private RFX1_CustomDistortionImpl customustomDistortionImpl;
    12.  
    13.  
    14.     public ScriptableRenderPass GetPassToEnqueue(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorHandle, RenderTargetHandle depthHandle)
    15.     {
    16.         if (customustomDistortionImpl == null) customustomDistortionImpl = new RFX1_CustomDistortionImpl();
    17.         customustomDistortionImpl._baseDescriptor = baseDescriptor;
    18.         customustomDistortionImpl._colorHandle = colorHandle;
    19.         customustomDistortionImpl._depthHandle = depthHandle;
    20.         customustomDistortionImpl.IsActive = IsActive;
    21.         return customustomDistortionImpl;
    22.     }
    23. }
    24.  
    25.  
    26. public class RFX1_CustomDistortionImpl : ScriptableRenderPass
    27. {
    28.     public bool IsActive = true;
    29.     const string transparentsTag = "Render Distorted Transparent";
    30.     const string copyColorTag = "Copy Transparent Color";
    31.  
    32.     public RenderTextureDescriptor _baseDescriptor;
    33.     public RenderTargetHandle _colorHandle;
    34.     public RenderTargetHandle _depthHandle;
    35.  
    36.     private RenderTargetHandle _destination;
    37.  
    38.     public RFX1_CustomDistortionImpl()
    39.     {
    40.         RegisterShaderPassName("CustomDistortion");
    41.         _destination.Init("_GrabTexture");
    42.     }
    43.  
    44.     public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData)
    45.     {
    46.         if (!IsActive) return;
    47.          //copy opaque + transparent color
    48.  
    49.         CommandBuffer cmd = CommandBufferPool.Get(copyColorTag);
    50.  
    51.         RenderTargetIdentifier colorRT = _colorHandle.Identifier();
    52.         RenderTargetIdentifier opaqueColorRT = _destination.Identifier();
    53.         RenderTextureDescriptor opaqueDesc = ScriptableRenderer.CreateRenderTextureDescriptor(ref renderingData.cameraData, 1);
    54.  
    55.         cmd.GetTemporaryRT(_destination.id, opaqueDesc);
    56.         cmd.Blit(colorRT, opaqueColorRT);
    57.    
    58.         context.ExecuteCommandBuffer(cmd);
    59.         CommandBufferPool.Release(cmd);
    60.  
    61.  
    62.         //draw custom distortion with transparent
    63.  
    64.         cmd = CommandBufferPool.Get(transparentsTag);
    65.         using (new ProfilingSample(cmd, transparentsTag))
    66.         {
    67.             var loadOp = RenderBufferLoadAction.Load;
    68.             var storeOp = RenderBufferStoreAction.Store;
    69.             SetRenderTarget(cmd, _colorHandle.Identifier(), loadOp, storeOp,
    70.                 _depthHandle.Identifier(), loadOp, storeOp, ClearFlag.None, Color.black, _baseDescriptor.dimension);
    71.  
    72.             context.ExecuteCommandBuffer(cmd);
    73.             cmd.Clear();
    74.  
    75.             Camera camera = renderingData.cameraData.camera;
    76. #if UNITY_2019_1_OR_NEWER
    77.             DrawingSettings drawingSettings = new DrawingSettings(new ShaderTagId("CustomDistortion"), new SortingSettings(camera));
    78.             FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.transparent);
    79.             context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings);
    80. #else
    81.             var drawSettings = CreateDrawRendererSettings(camera, SortFlags.CommonTransparent, RendererConfiguration.None, renderingData.supportsDynamicBatching);
    82.             var transparentFilterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.transparent };
    83.             context.DrawRenderers(renderingData.cullResults.visibleRenderers, ref drawSettings, transparentFilterSettings);
    84. #endif
    85.         }
    86.  
    87.         context.ExecuteCommandBuffer(cmd);
    88.         CommandBufferPool.Release(cmd);
    89.     }
    90.  
    91.     public override void FrameCleanup(CommandBuffer cmd)
    92.     {
    93.         base.FrameCleanup(cmd);
    94.  
    95.         cmd.ReleaseTemporaryRT(_destination.id);
    96.     }
    97.  
    98. }


    For HDRP it's more simple.
    You just need add additional pass and write distortion offset uv to output.


    Code (CSharp):
    1.  
    2. Properties
    3.    {
    4. ....
    5. _Distortion("Distortion", Float) = 10
    6. .....
    7. }
    8.  
    9. Pass
    10.     {
    11.         Tags{ "LightMode" = "DistortionVectors" }
    12.         Blend One One
    13.         BlendOp Add, Add
    14.         Cull Off
    15.         ZWrite Off
    16.         ZTest LEqual
    17.  
    18. .....
    19. float _Distortion;
    20. half4 frag(v2f i) : SV_Target
    21. {
    22. ....
    23. half3 dist = UnpackNormal(tex2D(_NormalTex, i.uv));
    24. return float4(dist * 0.1 * _Distortion, 1, 0); // I not sure how it works, but with 0.1 it's have correct distortion strength.
    25. }
    26.  
    27. Pass
    28.    {
    29.            Blend SrcAlpha OneMinusSrcAlpha
    30.  
    31. //distortion code
    32. }
    33.  
    34.  
     
    asdzxcv777 and Elringus like this.
  9. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    Nice, thanks for sharing the examples! If I've got it right, you're copying screen texture after the transparent passes and using it as the _GrabTexture in the distortion shader. But does multiple instances of that shader "see" each other when stacked? I mean, if you create two objects with this distortion material and position them one behind another, will the one closer to the camera actually receive screen texture with another object present on it?
     
  10. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    After fiddling with the latest version of LWRP I was able to finally create a custom render pass, which is invoked after the transparent queue, grabs camera texture, assigns it to a global shader texture property and then renders my custom objects, which are using the grabbed texture.



    It doesn't solve the issue with stacking the objects (which is probably impossible with LWRP after all), though.

    Here is an example, in case anyone is interested (Unity still lacks documentation for this stuff): https://gist.github.com/Elringus/69f0da9c71306f1ea0f575cc7568b31a
     
    Allarius and Bip901 like this.
  11. unityuserunity85496

    unityuserunity85496

    Joined:
    Jan 9, 2019
    Posts:
    89
    Well that was Soooooo not obvious. Thankyou for this @Elringus
     
    Elringus likes this.
  12. Bip901

    Bip901

    Joined:
    May 18, 2017
    Posts:
    71
    @Elringus Thank you so much! I write shaders using ShaderGraph. Do you have an idea how I could tag my shader with the "MyCustomPass" tag (link)?