Writing into a render texture using depth buffer from another render texture, how to do it?

I have 2 cameras, first one is the main camera and it draws all the geometry as usual, and the second one is a light camera that draws some 2d lights. And then i have a post effect that Blits those two textures.

So the problem is, i need to render some transparent geometry (some 2d light) into a render texture, lets call it ‘LightRT’. But i need to occlude it with the depth buffer values from what was rendered by main camera, which renders into RenderTexture.active.

I’m trying to do it like this, in the image effect:

protected void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
     
        Graphics.SetRenderTarget(LightRT.colorBuffer, source.depthBuffer);

        ImageEffectCamera.Render();

        Graphics.SetRenderTarget(source.colorBuffer, source.depthBuffer);
        Material.SetTexture("_Overlay", LightRT);
        Graphics.Blit(source, destination, Material);

     
    }

and in the shader of the light i set ZTest to LEqual, which should occlude the light, right?

Light shader:

Shader "01DEVS/Light 2D/Screen"
{
    Properties
    {
        _Color("Color (RGBA)",Color) = (1,1,1,1)
        _MainTex ("Lightmap (RGBA)", 2D) = "white" {}
        [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)]_StencilOp ("Stencil Operation", Float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "RenderType"="Light"
        }
        Pass
        {
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Name "BASE"
            Cull Back
            Lighting Off
            ZWrite Off
            ZTest LEqual
            ColorMask RGBA
            Blend OneMinusDstColor One
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
        
            #include "UnityCG.cginc"
        
         
            sampler2D _MainTex;
            half4 _MainTex_ST;
            fixed4 _Color;
            struct appdata_mine {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                fixed4 color : COLOR;
            };
        
            struct v2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
              
            };
        
            v2f vert( appdata_mine v ){
                v2f o;
                o.vertex = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = TRANSFORM_TEX( v.texcoord, _MainTex );
                o.color = v.color * _Color;
              
                return o;
            }
        
            half4 frag( v2f i ) : COLOR
            {
                return i.color;
            }
        
            ENDCG
        }
    }
}

The light is not occluded, and i have no idea why, can anyone explain why?

I also tried to enable depth rendering in main camera, like this:

CachedCamera.depthTextureMode = DepthTextureMode.Depth;

and assign the LightRT render texture into which the light is rendered to the camera which renders the light,
and then in the light shader to compare the computed light’s depth value to the one present in the _CameraDepthTexture, like this:

Shader "01DEVS/Light 2D/Screen"
{
    Properties
    {
        _Color("Color (RGBA)",Color) = (1,1,1,1)
        _MainTex ("Lightmap (RGBA)", 2D) = "white" {}
        [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)]_StencilOp ("Stencil Operation", Float) = 0
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "RenderType"="Light"
        }
        Pass
        {
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Name "BASE"
            Cull Back
            Lighting Off
            ZWrite Off
            ZTest Always
            ColorMask RGBA
            Blend OneMinusDstColor One
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
      
            #include "UnityCG.cginc"
      
       
            sampler2D _CameraDepthTexture;

            sampler2D _MainTex;
            half4 _MainTex_ST;
            fixed4 _Color;
            struct appdata_mine {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                fixed4 color : COLOR;
            };
      
            struct v2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
                float4 projPos : TEXCOORD1;
            };
      
            v2f vert( appdata_mine v ){
                v2f o;
                o.vertex = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = TRANSFORM_TEX( v.texcoord, _MainTex );
                o.color = v.color * _Color;
                o.projPos = ComputeScreenPos(o.vertex);
                return o;
            }
      
            half4 frag( v2f i ) : COLOR
            {
                //Get the distance to the camera from the depth buffer for this point
                float sceneZ = LinearEyeDepth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r);
                float partZ = i.projPos.z;
                if(partZ > sceneZ) return half4(1,0,0,1);
                return half4(0,1,0,1);
            }
      
            ENDCG
        }
    }
}

so i output green if the fragment is in front of the geometry, and red if it is behind. All i get is green, i.e. like there is no depth information in the _CameraDepthTexture.

The code in the image effect is as follows:

protected void OnRenderImage(RenderTexture source, RenderTexture destination)
    {   
        ImageEffectCamera.Render();
        Material.SetTexture("_Overlay", LightRT);
        Graphics.Blit(source, destination, Material);
   
    }

I don’t get what is the problem, can anyone help?

So i set up very simple scene, i have a cube and a quad that intersects it, the quad is set to separate layer that is rendered only by the light camera, and has a light shader on it.


notice that in the scene view, scene view camera is rendering all layers, the Light layer and the cube layer, so i get the correct results, where the light quad is culled by the cube, it gets red. But in the game view, the depth information is missing, so i get green everywhere. The image effect just multiplies the light texture with the texture generated by the main camera.

This is what the frame debugger is showing:
Depth rendering: (notice the render texture name, Camera DepthTexture)


Main camera rendering (why is the depth buffer cleared?) (notice the render texture name, ImageEffects Temp)

Image effect(light) camera rendering: (notice the render texture name, LightmapData Render Texture 2)

And the blitting:

So it seems that the depth information written by the camera (Camera.depthTextureMode = DepthTextureMode.Depth) is written to a render texture called “Camera DepthTexture”
Then main camera renders the cube into “ImageEffects Temp”
and then the image effect is rendered into “LightmapData Render Texture 2” which is created by me.
When i acces _CameraDepthTexture in the light shader, which depth texture is it? because it does not seem to be the “Camera DepthTexture”, because the result in the game view is all green, when it should be red where the cube is.

I have tried to use the colorbuffer from “LightmapData Render Texture 2” and the depth buffer from the RenderTexture.active ( i wrote about this i nthe first post), but i can’t get it to work.

Any ideas?

Does your light camera’s render target have both color and depth buffers? If so, can you make it color buffer only (by setting depth to 0 in the RenderTexture constructor)?

Also, what’s the clear settings for the light camera? You could say “don’t clear” and do a manual clear of the color buffer prior to rendering via GL.Clear

I had to do something similar recently (use the main scene’s depth buffer with another color buffer), but I didn’t have a secondary camera. I submitted the geometry I needed via DrawMeshNow (instead of using quads on a separate layer).

1 Like

Yes, it has a depth buffer, but i can’t remove it because i need the stencil, which as far as i know is bound with depth buffer.

That is exactly what saved my case(and something more, later on it), automatic clearing was messing things up, it was clearing the wrong texture.

So i managed to solve the problem with your advice plus something extra, that extra is

ImageEffectCamera.SetTargetBuffers(RenderTexture.colorBuffer, source.depthBuffer);

this allows me to set the color buffer from the light camera(ImageEffectCamera) render target, and depth buffer from the screen. I also do not need the main camera to render the depth into separate depth texture, which is very nice.

This is the result:
Image effect camera clear flags set to nothing.
My image effect code:

protected void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        //set our lightmap texture as active render texture
        var active = RenderTexture.active;
        RenderTexture.active = RenderTexture;
        //clear the lightmap
        GL.Clear(true, true, Color.clear);
        RenderTexture.active = active;
        //RenderTexture is the target texture of the ImageEffectCamera
        ImageEffectCamera.SetTargetBuffers(RenderTexture.colorBuffer, source.depthBuffer);
        ImageEffectCamera.Render();

        Material.SetTexture("_Overlay", RenderTexture);
        Graphics.Blit(source, destination, Material, (int)_blendMode);  
    }

Light shader:

Shader "01DEVS/Light 2D/Screen"
{
    Properties
    {
        _Color("Color (RGBA)",Color) = (1,1,1,1)
        _MainTex ("Lightmap (RGBA)", 2D) = "white" {}
        [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)]_StencilOp ("Stencil Operation", Float) = 0
    }
  
    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "RenderType"="Light"
        }
        Pass
        {
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Name "BASE"
            Cull Back
            Lighting Off
            ZWrite Off
            ZTest LEqual
            ColorMask RGBA
            Blend OneMinusDstColor One
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
          
            #include "UnityCG.cginc"
          
           
            sampler2D _CameraDepthTexture;

            sampler2D _MainTex;
            half4 _MainTex_ST;
            fixed4 _Color;
            struct appdata_mine {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                fixed4 color : COLOR;
            };
          
            struct v2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };
          
            v2f vert( appdata_mine v ){
                v2f o;
                o.vertex = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = TRANSFORM_TEX( v.texcoord, _MainTex );
                o.color = v.color * _Color;
                return o;
            }
          
            half4 frag( v2f i ) : COLOR
            {
                half4 overlay = tex2D( _MainTex, i.uv ) * i.color;
                return overlay;
            }
          
            ENDCG
        }
    }
}

I dont need to do manual ZTesting, because the dept hbuffer the light is tested against is from the main camera, ZTest LEqual does everything for me, which is also very nice.

And here are the frame debugger results:
ImageEffects Temp happens to be the screen buffer (to which the main camera is rendering), this is also the source RenderTexture which comes into the OnRenderImage method.

Notice that the renderTarget of the light’s camera is being cleared (the manual clearing we did in the OnRenderImage method)

You can see that the depth buffer of the light’s camera renderTarget is the one from the main camera, because we set it before rendering the light quad.
This bit of code:

ImageEffectCamera.SetTargetBuffers(RenderTexture.colorBuffer, source.depthBuffer);

Notice that frame debugger is set to show the depth texture

And the final result after blitting:

Thanks for the help!

1 Like

Thank you so much for sharing! Took me AAAGGESS to get this to work, but there was a subtle difference in my frame debugger (big thanks for sharing yours!).

2999341--223565--Screen Shot 2017-03-19 at 13.56.09.png

I had “Camera.AAResolve” appearing in my stack which caused the ZBuffer to get corrupted by the time I could render my cube.

2999341--223566--Screen Shot 2017-03-19 at 13.53.25.png

I disabled Anti-Aliasing in my quality settings and BINGO! It worked a charm :slight_smile:

2999341--223567--Screen Shot 2017-03-19 at 14.00.21.png

Thanks for sharing and if anyone has any corruption in their Depth buffer, try disabling Anti-Aliasing!

1 Like

I have to thank you both for the advices, i managed to reuse the main camera depth buffer with SetTargetBuffers + Antialiasing off, that fix the rendering of color buffer; However, depth testing wasn’t working because the camera was clearing the z+color+stencil before rendering so i had to turn clearflags off and do it manually (as iSinner did).

Anyway, any idea on how to enable antialiasing + shared depth buffer ?

We ended up keeping anti-aliasing turned off, and haven’t yet found a work around for it sadly. Glad someone else got some benefit from this thread! :slight_smile:

1 Like

I guess i ll look for FXAA on mobile instead of MSAA

I’ve tried to use customRenderTexture + Display.main.depthBuffer
in CommandBuffer.SetRenderTarget( ). Ends up with this error: You’re trying to mix color and depth buffers from RenderTexture and from screen.

The solution is:Write my code in OnRenderImage(RenderTexture source, RenderTexture destination)
and use customRenderTexture + source.depthBuffer. It works fine ! Showing that source.depthBuffer is not a real screenDepthBuffer.