Search Unity

Transparent-Friendly Depth Fog

Discussion in 'General Graphics' started by Boolet, Jun 29, 2021.

  1. Boolet

    Boolet

    Joined:
    Feb 18, 2018
    Posts:
    4
    I am trying to create a volumetric-like fog effect that will play nice with the Unity standard shader, including transparent objects.

    My current approach for the fog is to render the backfaces of the box in which I want the fog to be, at render order Transparent+1, using an alpha blend. The strength of the fog effect attenuates based on the distance from the box, so when the camera is outside of the box it is completely invisible.

    Inside the box however, the alpha of the grey fog color is determined using the camera's depth texture, where increased depth is increased alpha. This works great on scenes composed of opaque objects, but because transparent objects do not write to the depth texture the fog alpha for their pixel locations is calculated using the depth of the objects behind them.

    I am using Unity 2018, and my proposed solution is to use the command buffer to add some extra steps to the render; setting this up is the part I need help with. The steps I want to add are:
    1. Draw the transparent objects with an opaque material to a separate depth texture.
    2. Add an extra fog pass to draw the amount of fog between the two depth textures, before transparent objects are drawn.
    3. Draw the remaining fog on top of the transparent objects.

    I'm fairly new to shaders, and extremely new to the command buffer. If anyone can give me some tips it would be much appreciated! Here is my command buffer code so far.

    Code (CSharp):
    1. public class BlitTransparentDepth : MonoBehaviour
    2. {
    3.     [SerializeField] Camera cam = null;
    4.     [SerializeField] Renderer transparentRenderer = null;
    5.     [SerializeField] Material depthMaterial = null;
    6.  
    7.     bool bufferAdded = false;
    8.     CommandBuffer transparentDepthCommands = null;
    9.  
    10.     private void OnEnable() {
    11.         if(!bufferAdded)
    12.             AddCommandsToCamera();
    13.     }
    14.  
    15.     private void OnDisable() {
    16.         if(bufferAdded && cam != null)
    17.             cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, transparentDepthCommands);
    18.     }
    19.  
    20.     void AddCommandsToCamera() {
    21.         SetupCommandBuffer();
    22.         cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, transparentDepthCommands);
    23.         bufferAdded = true;
    24.     }
    25.  
    26.     void SetupCommandBuffer() {
    27.         int depthID = Shader.PropertyToID("_TransparentDepth");
    28.         int colorID = Shader.PropertyToID("_TransparentColor");
    29.         transparentDepthCommands = new CommandBuffer();
    30.         transparentDepthCommands.GetTemporaryRT(depthID, cam.pixelWidth, cam.pixelHeight, 0, FilterMode.Point, RenderTextureFormat.RFloat, RenderTextureReadWrite.Linear);
    31.         //RenderBufferLoadAction.DontCare? Or Load?
    32.         transparentDepthCommands.SetRenderTarget(colorID, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, depthID, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store);
    33.         //commandBuffer.SetGlobalTexture(
    34.         transparentDepthCommands.DrawRenderer(transparentRenderer, depthMaterial);
    35.     }
    36. }
    To test this code I've written just a simple depth texture sampling shader, which I would like to see the resulting texture on.

    Code (CSharp):
    1. Shader "Unlit/DepthVisualizer"
    2. {
    3.     Properties
    4.     {
    5.         _WhiteoutDistance ("Whiteout Distance", Float) = 25
    6.     }
    7.     SubShader
    8.     {
    9.         Tags {"Queue" = "Transparent+1"}
    10.         ZWrite Off ZTest Always
    11.  
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.  
    18.             #include "UnityCG.cginc"
    19.  
    20.             struct appdata
    21.             {
    22.                 float4 vertex : POSITION;
    23.                 float2 uv : TEXCOORD0;
    24.             };
    25.  
    26.             struct v2f
    27.             {
    28.                 float2 uv : TEXCOORD0;
    29.             };
    30.  
    31.             float _WhiteoutDistance;
    32.             sampler2D _TransparentDepth;
    33.  
    34.             v2f vert (appdata v, out float4 pos : SV_POSITION)
    35.             {
    36.                 v2f o;
    37.                 pos = UnityObjectToClipPos(v.vertex);
    38.                 o.uv = v.uv;
    39.                 return o;
    40.             }
    41.  
    42.             fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
    43.             {
    44.  
    45.                 float2 screenUV = screenPos.xy / _ScreenParams.xy;
    46.                 screenUV.y = 1 - screenUV.y;    //something causes the depth texture to be upside down
    47.                                                 //in either scene view or game view.
    48.  
    49.                 float depth = UNITY_SAMPLE_DEPTH(tex2D(_TransparentDepth, screenUV));
    50.                 depth = LinearEyeDepth(depth) / _WhiteoutDistance;
    51.                 return fixed4(depth, depth, depth, 1);
    52.             }
    53.             ENDCG
    54.         }
    55.     }
    56. }
    57.  
    Thanks so much!