HDRP Multi-camera rendering color / depth / motion to textures errors

Background:
We are trying to collect simulation data using Unity using HDRP scriptable rendering. From multiple cameras, we would like to serialize (to file system) the color, depth, and motion vectors textures from these cameras.

Here is a sample (good) output from a single camera (Imgur: The magic of the Internet):

Approach:
We have a camera script (attached) which leverages 3 RenderTextures (for color, depth, and motion vectors), and uses the RenderPipelineManager.endCameraRendering callback to blit the camera’s active texture into the render textures before using ReadPixels to copy the render textures into Texture2D data for serialization into a file. The camera is configured with setTargetBuffers, and has the Depth and MotionVectors flags set in its depthTextureMode. When performing the blitting, a material / shader combination is used in conjunction with the depth and motionvectors pass to copy out the _CameraDepthTexture and the _CameraMotionVectorsTexture into the target render texture. The associated DepthShader and MotionShader are also provided.

Problems Found:

  1. The script / shaders / materials used (essentially the whole pipeline) only works on mac right now - on linux (Vulkan) and Windows (DX11 or DX12 experimental) the motion vectors and depth are all black. Only on mac (Metal) does any of this work.

  2. Planar Reflection Probes in the scene mess up the depth / motion vectors output (I have attached an example of this) - the only way to fix this issue is to disable the planar reflection probes in the scene. The first frame of RGB (both what is viewed in the game window and what is saved is also messed up the same way)

initial frames messed up (Imgur: The magic of the Internet):


subsequent depth / motion vector frames are still messed up, but RGB is good now (Imgur: The magic of the Internet):

Of course if we disable the planar reflection probes, then all frames are good - even initial frames. We do have other reflection probes in the scene, and everything works fine with them.

  1. If we do not use setTargetBuffers, but instead set the camera’s targetTexture, we do not get depth or motion vectors (and of course in this case we ensure the targetTexture has depth).

  2. The cameras do not render the correct view - all views (front, back, left, right) are rendered, but they are not associated with the correct image data; e.g. front image shows back view, right image shows front view, etc. In the scene view, each camera is oriented properly, and the preview shows the proper rendering.

imgur post with some images: Unity HDRP Multi-Camera rendering bugs - Album on Imgur

5576731–575767–UnityCamera.cs (13.5 KB)
5576731–575773–DepthShader.shader (1.38 KB)
5576731–575776–MotionShader.shader (1.55 KB)

As a note - I’ve tried this on multiple scenes, and with multiple versions of Unity and HDRP. For reference here are the versions I’ve tried:

Unity:

  • 2019.3.1f1
  • 2019.3.2f1
  • 2019.3.3f1
  • 2019.3.4f1
  • 2020.1.0a25

HDRP:

  • 7.1.8
  • 7.2.1
  • 8.0.1

Here is an updated file which simplifies some of the code into a single script to show how multiple cameras are handled

5582530–576523–DataCollector.cs (10.7 KB)

1 Like

Update:

I’ve determined that the _CameraDepthTexture and _CameraMotionVectorsTexture on Windows have no valid data.

When I use this output for my fragment shader:

return float4(0.2, 0.4, 0.6, 1.0);

I get this data when reading the serialized files (assuming it’s [Depth, Motion X, Motion Y, UNUSED]):

Loading newer format _depth_motion file
Depth (min, max): (0.2, 0.2)
Motion x (min, max): (0.4, 0.4)
Motion y (min, max): (0.6, 0.6)

However, when I use this as my shader:

      sampler2D_float _CameraDepthTexture;
      sampler2D_half _CameraMotionVectorsTexture;

      struct v2f {
         float4 pos : SV_POSITION;
         float4 scrPos : TEXCOORD1;
      };

      // Vertex Shader
      v2f vert(appdata_base v) {
         v2f o;
         o.pos = UnityObjectToClipPos(v.vertex);
         o.scrPos = ComputeScreenPos(o.pos);
         return o;
      }

      // Fragment Shader
      float4 frag(v2f i) : SV_TARGET {
        float2 coords = UNITY_PROJ_COORD(i.scrPos);
        float depth = Linear01Depth(tex2D(_CameraDepthTexture, coords).r) * _ProjectionParams.z;
        half2 motion = tex2D(_CameraMotionVectorsTexture, coords).rg;

        return float4(depth, motion.r, motion.g, 1.0);
    }

Then I get this from ALL of my serialized files (even in cases where I am very close to objects and where I am moving):

Loading newer format _depth_motion file
Depth (min, max): (0.046333157, 0.046333157)
Motion x (min, max): (0.21582031, 0.21582031)
Motion y (min, max): (0.21582031, 0.21582031)

I’ve attached my updated shader code and data collector script for reference.

5595220–578419–DataCollector.cs (10.2 KB)

Further update: I’ve determined that the approach I was using (RenderPipelineManager::endCameraRendering) is not viable within HDRP, whereas it seems that creating a custom HDRP post process according to the docs here: Custom Pass | High Definition RP | 8.0.1 might be viable. As it is, I am able to create a CustomPostProcessVolumeComponent which applies the shader to all cameras in the scene (which I can then filter as needed) and I can see that the shader is working in the editor in Windows (progress!). Currently looking through the docks on the RTHandle API so that I can copy the resultant textures from the HDUtils.DrawFullScreen calls back into T2Ds.

Update: actually using HDRP now i have it behaving the same on windows and mac now. This means that I’m able to get motion / depth / color data on all platforms, but I still have the issue that the cameras are not rendering the right data:

This IMGUR album shows the scene config (HDRP sample scene with minimal config) as well as the resultant data (after moving a little bit to get different images in frame):

Note - i believe the main camera and the depth configuration of the cameras is affecting the rendering output.

Here is an example of what the attached shader and script produce now (note - this should be from the front camera but is actually duplicating as both the left and right camera):

I did manage to get it fixed - turns out Graphics.Blit should not be used, and instead cmd.Blit should be used. Also the order of blitting and when readPixels is called matters. I’ve attached the updated script / shader for completeness.

This album (Imgur: The magic of the Internet) has some example images showing the proper data, here is one example image:

5599090–578968–ColorPostProcess.shader (1.96 KB)
5599090–578971–DepthMotionPostProcess.shader (2.18 KB)
5599090–578974–DataCollectorPostProcess.cs (10.8 KB)

1 Like