网上演练
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
12 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace UTJ.FrameCapturer
{
[AddComponentMenu("UTJ/FrameCapturer/GBuffer Recorder")]
[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class GBufferRecorder : RecorderBase
{
#region inner_types
[Serializable]
public struct FrameBufferConponents
{
public bool frameBuffer;
public bool GBuffer;
public bool albedo;
public bool occlusion;
public bool specular;
public bool smoothness;
public bool normal;
public bool emission;
public bool depth;
public bool velocity;
public static FrameBufferConponents default_value
{
get
{
var ret = new FrameBufferConponents
{
frameBuffer = true,
GBuffer = true,
albedo = true,
occlusion = true,
specular = true,
smoothness = true,
normal = true,
emission = true,
depth = true,
velocity = true,
};
return ret;
}
}
}
class BufferRecorder
{
RenderTexture m_rt;
int m_channels;
int m_targetFramerate = 30;
string m_name;
MovieEncoder m_encoder;
public BufferRecorder(RenderTexture rt, int ch, string name, int tf)
{
m_rt = rt;
m_channels = ch;
m_name = name;
}
public bool Initialize(MovieEncoderConfigs c, DataPath p)
{
string path = p.GetFullPath() + "/" + m_name;
c.Setup(m_rt.width, m_rt.height, m_channels, m_targetFramerate);
m_encoder = MovieEncoder.Create(c, path);
return m_encoder != null && m_encoder.IsValid();
}
public void Release()
{
if(m_encoder != null)
{
m_encoder.Release();
m_encoder = null;
}
}
public void Update(double time)
{
if (m_encoder != null)
{
fcAPI.fcLock(m_rt, (data, fmt) =>
{
m_encoder.AddVideoFrame(data, fmt, time);
});
}
}
}
#endregion
#region fields
[SerializeField] MovieEncoderConfigs m_encoderConfigs = new MovieEncoderConfigs(MovieEncoder.Type.Exr);
[SerializeField] FrameBufferConponents m_fbComponents = FrameBufferConponents.default_value;
[SerializeField] Shader m_shCopy;
Material m_matCopy;
Mesh m_quad;
CommandBuffer m_cbCopyFB;
CommandBuffer m_cbCopyGB;
CommandBuffer m_cbClearGB;
CommandBuffer m_cbCopyVelocity;
RenderTexture m_rtFB;
RenderTexture[] m_rtGB;
List<BufferRecorder> m_recorders = new List<BufferRecorder>();
#endregion
#region properties
public FrameBufferConponents fbComponents
{
get { return m_fbComponents; }
set { m_fbComponents = value; }
}
public MovieEncoderConfigs encoderConfigs { get { return m_encoderConfigs; } }
#endregion
public override bool BeginRecording()
{
if (m_recording) { return false; }
if (m_shCopy == null)
{
Debug.LogError("GBufferRecorder: copy shader is missing!");
return false;
}
m_outputDir.CreateDirectory();
if (m_quad == null) m_quad = fcAPI.CreateFullscreenQuad();
if (m_matCopy == null) m_matCopy = new Material(m_shCopy);
var cam = GetComponent<Camera>();
if (cam.targetTexture != null)
{
m_matCopy.EnableKeyword("OFFSCREEN");
}
else
{
m_matCopy.DisableKeyword("OFFSCREEN");
}
int captureWidth = cam.pixelWidth;
int captureHeight = cam.pixelHeight;
GetCaptureResolution(ref captureWidth, ref captureHeight);
if (m_encoderConfigs.format == MovieEncoder.Type.MP4 ||
m_encoderConfigs.format == MovieEncoder.Type.WebM)
{
captureWidth = (captureWidth + 1) & ~1;
captureHeight = (captureHeight + 1) & ~1;
}
if (m_fbComponents.frameBuffer)
{
m_rtFB = new RenderTexture(captureWidth, captureHeight, 0, RenderTextureFormat.ARGBHalf);
m_rtFB.wrapMode = TextureWrapMode.Repeat;
m_rtFB.Create();
int tid = Shader.PropertyToID("_TmpFrameBuffer");
m_cbCopyFB = new CommandBuffer();
m_cbCopyFB.name = "GBufferRecorder: Copy FrameBuffer";
m_cbCopyFB.GetTemporaryRT(tid, -1, -1, 0, FilterMode.Point);
m_cbCopyFB.Blit(BuiltinRenderTextureType.CurrentActive, tid);
m_cbCopyFB.SetRenderTarget(m_rtFB);
m_cbCopyFB.DrawMesh(m_quad, Matrix4x4.identity, m_matCopy, 0, 0);
m_cbCopyFB.ReleaseTemporaryRT(tid);
cam.AddCommandBuffer(CameraEvent.AfterEverything, m_cbCopyFB);
}
if (m_fbComponents.GBuffer)
{
m_rtGB = new RenderTexture[8];
for (int i = 0; i < m_rtGB.Length; ++i)
{
m_rtGB[i] = new RenderTexture(captureWidth, captureHeight, 0, RenderTextureFormat.ARGBHalf);
m_rtGB[i].filterMode = FilterMode.Point;
m_rtGB[i].Create();
}
// clear gbuffer (Unity doesn't clear emission buffer - it is not needed usually)
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);
// copy gbuffer
m_cbCopyGB = new CommandBuffer();
m_cbCopyGB.name = "GBufferRecorder: Copy GBuffer";
m_cbCopyGB.SetRenderTarget(new RenderTargetIdentifier[] {
m_rtGB[0], m_rtGB[1], m_rtGB[2], m_rtGB[3], m_rtGB[4], m_rtGB[5], m_rtGB[6]
}, m_rtGB[0]);
m_cbCopyGB.DrawMesh(m_quad, Matrix4x4.identity, m_matCopy, 0, 2);
cam.AddCommandBuffer(CameraEvent.BeforeGBuffer, m_cbClearGB);
cam.AddCommandBuffer(CameraEvent.BeforeLighting, m_cbCopyGB);
if (m_fbComponents.velocity)
{
m_cbCopyVelocity = new CommandBuffer();
m_cbCopyVelocity.name = "GBufferRecorder: Copy Velocity";
m_cbCopyVelocity.SetRenderTarget(m_rtGB[7]);
m_cbCopyVelocity.DrawMesh(m_quad, Matrix4x4.identity, m_matCopy, 0, 4);
cam.AddCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, m_cbCopyVelocity);
cam.depthTextureMode = DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
}
}
int framerate = m_targetFramerate;
if (m_fbComponents.frameBuffer) { m_recorders.Add(new BufferRecorder(m_rtFB, 4, "FrameBuffer", framerate)); }
if (m_fbComponents.GBuffer)
{
if (m_fbComponents.albedo) { m_recorders.Add(new BufferRecorder(m_rtGB[0], 3, "Albedo", framerate)); }
if (m_fbComponents.occlusion) { m_recorders.Add(new BufferRecorder(m_rtGB[1], 1, "Occlusion", framerate)); }
if (m_fbComponents.specular) { m_recorders.Add(new BufferRecorder(m_rtGB[2], 3, "Specular", framerate)); }
if (m_fbComponents.smoothness) { m_recorders.Add(new BufferRecorder(m_rtGB[3], 1, "Smoothness", framerate)); }
if (m_fbComponents.normal) { m_recorders.Add(new BufferRecorder(m_rtGB[4], 3, "Normal", framerate)); }
if (m_fbComponents.emission) { m_recorders.Add(new BufferRecorder(m_rtGB[5], 3, "Emission", framerate)); }
if (m_fbComponents.depth) { m_recorders.Add(new BufferRecorder(m_rtGB[6], 1, "Depth", framerate)); }
if (m_fbComponents.velocity) { m_recorders.Add(new BufferRecorder(m_rtGB[7], 2, "Velocity", framerate)); }
}
foreach (var rec in m_recorders)
{
if (!rec.Initialize(m_encoderConfigs, m_outputDir))
{
EndRecording();
return false;
}
}
base.BeginRecording();
Debug.Log("GBufferRecorder: BeginRecording()");
return true;
}
public override void EndRecording()
{
foreach (var rec in m_recorders) { rec.Release(); }
m_recorders.Clear();
var cam = GetComponent<Camera>();
if (m_cbCopyFB != null)
{
cam.RemoveCommandBuffer(CameraEvent.AfterEverything, m_cbCopyFB);
m_cbCopyFB.Release();
m_cbCopyFB = null;
}
if (m_cbClearGB != null)
{
cam.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, m_cbClearGB);
m_cbClearGB.Release();
m_cbClearGB = null;
}
if (m_cbCopyGB != null)
{
cam.RemoveCommandBuffer(CameraEvent.BeforeLighting, m_cbCopyGB);
m_cbCopyGB.Release();
m_cbCopyGB = null;
}
if (m_cbCopyVelocity != null)
{
cam.RemoveCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, m_cbCopyVelocity);
m_cbCopyVelocity.Release();
m_cbCopyVelocity = null;
}
if (m_rtFB != null)
{
m_rtFB.Release();
m_rtFB = null;
}
if (m_rtGB != null)
{
foreach (var rt in m_rtGB) { rt.Release(); }
m_rtGB = null;
}
if (m_recording)
{
Debug.Log("GBufferRecorder: EndRecording()");
}
base.EndRecording();
}
#region impl
#if UNITY_EDITOR
void Reset()
{
m_shCopy = fcAPI.GetFrameBufferCopyShader();
}
#endif // UNITY_EDITOR
IEnumerator OnPostRender()
{
if (m_recording)
{
yield return new WaitForEndOfFrame();
//double timestamp = Time.unscaledTime - m_initialTime;
double timestamp = 1.0 / m_targetFramerate * m_recordedFrames;
foreach (var rec in m_recorders) { rec.Update(timestamp); }
++m_recordedFrames;
}
m_frame++;
}
#endregion
}
}