Render GBuffer to RenderTexture

I’m trying to record some GBuffer information from a camera when it’s rendered, and save it into a RenderTexture format. Similar to the debug views in the SceneView, I’d like to capture the Albdeo, Smoothness, Specular, and Normals from the GBuffer for later use. I’m pretty sure this is possible with CommandBuffers but I’m not sure how to use them. I’ve attached my little attempt at it, but I just get a bunch of black rendertextures from it.

I’m trying to cannibalise a bit of this project: GitHub - unity3d-jp/FrameCapturer: export framebuffer, GBuffer or any RenderTextures from Unity to file. supported format: png, exr, gif, webm, mp4

The shader field in the class below is a 1:1 copy of the shader in the above repo.

Yo @ - I think you wrote the above. Any thoughts?

using UnityEngine;
using UnityEngine.Rendering;

public class TerrainBasemap : MonoBehaviour
{
    public int Resolution = 1024;
    public Shader CaptureShader;

    public RenderTexture[] Textures = new RenderTexture[8];

    [ContextMenu("Recalculate")]
    public void Recalculate()
    {
        for (int i = 0; i < Textures.Length; i++)
        {
            var renderTexture = Textures[i];
            if (renderTexture != null)
            {
                DestroyImmediate(renderTexture);
            }
            renderTexture = new RenderTexture(Resolution, Resolution, 0, RenderTextureFormat.ARGBHalf);
            renderTexture.filterMode = FilterMode.Point;
            renderTexture.Create();
            Textures[i] = renderTexture;
        }

        var tmpOut = RenderTexture.GetTemporary(Resolution, Resolution, 0, RenderTextureFormat.ARGBHalf);
        var m_matCopy = new Material(CaptureShader);
        var m_quad = CreateFullscreenQuad();

        var cam = SetupCamera();
        cam.targetTexture = tmpOut;

        var m_cbClearGB = new CommandBuffer();
        m_cbClearGB.name = "GBufferRecorder: Cleanup GBuffer";
        if (cam.allowHDR)
        {
            m_cbClearGB.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
        }
        else
        {
            m_cbClearGB.SetRenderTarget(BuiltinRenderTextureType.GBuffer3);
        }
        m_cbClearGB.DrawMesh(m_quad, Matrix4x4.identity, m_matCopy, 0, 3);
        m_matCopy.SetColor("_ClearColor", cam.backgroundColor);
        cam.AddCommandBuffer(CameraEvent.BeforeGBuffer, m_cbClearGB);

        var cBuffer = new CommandBuffer();
        cBuffer.SetRenderTarget(new RenderTargetIdentifier[]
        {
            Textures[0], Textures[1], Textures[2], Textures[3], Textures[4], Textures[5], Textures[6]
        }, Textures[0]);
        cBuffer.DrawMesh(m_quad, Matrix4x4.identity, m_matCopy, 0, 0);
        cam.AddCommandBuffer(CameraEvent.BeforeLighting, cBuffer);

        cam.Render();
        cam.targetTexture = null;

        RenderTexture.ReleaseTemporary(tmpOut);
    }

    private void SetupLighting()
    {
        RenderSettings.ambientLight = Color.white;
        RenderSettings.ambientMode = AmbientMode.Flat;
    }

    private Camera SetupCamera()
    {
        var go = new GameObject();
        var cam = go.AddComponent<Camera>();

        var terrain = GetComponent<Terrain>();
        var tSize = terrain.terrainData.size;

        cam.orthographic = true;
        cam.orthographicSize = tSize.x/2;
        cam.nearClipPlane = 1;
        cam.farClipPlane = tSize.y*1.2f;

        cam.aspect = 1;
        cam.pixelRect = new Rect(0, 0, Resolution, Resolution);
    
        cam.transform.position = terrain.GetPosition() + new Vector3(tSize.x/2, tSize.y, tSize.z/2);
        cam.transform.rotation = Quaternion.LookRotation(Vector3.down);

        return cam;
    }

    public static Mesh CreateFullscreenQuad()
    {
        var r = new Mesh();
        r.vertices = new Vector3[4] {
                    new Vector3( 1.0f, 1.0f, 0.0f),
                    new Vector3(-1.0f, 1.0f, 0.0f),
                    new Vector3(-1.0f,-1.0f, 0.0f),
                    new Vector3( 1.0f,-1.0f, 0.0f),
                };
        r.triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
        r.UploadMeshData(true);
        return r;
    }
}

Orthographic camera does not support GBuffer, which was determined by the nature of GBuffer itself.