159 lines
6.6 KiB
C#
159 lines
6.6 KiB
C#
// SPDX-License-Identifier: MIT
|
|
#if GS_ENABLE_URP
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
#if UNITY_6000_0_OR_NEWER
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
#endif
|
|
|
|
namespace GaussianSplatting.Runtime
|
|
{
|
|
// Note: I have no idea what is the purpose of ScriptableRendererFeature vs ScriptableRenderPass, which one of those
|
|
// is supposed to do resource management vs logic, etc. etc. Code below "seems to work" but I'm just fumbling along,
|
|
// without understanding any of it.
|
|
//
|
|
// ReSharper disable once InconsistentNaming
|
|
class GaussianSplatURPFeature : ScriptableRendererFeature
|
|
{
|
|
class GSRenderPass : ScriptableRenderPass
|
|
{
|
|
const string GaussianSplatRTName = "_GaussianSplatRT";
|
|
#if !UNITY_6000_0_OR_NEWER
|
|
RTHandle m_RenderTarget;
|
|
internal ScriptableRenderer m_Renderer;
|
|
internal CommandBuffer m_Cmb;
|
|
#endif
|
|
|
|
public void Dispose()
|
|
{
|
|
#if !UNITY_6000_0_OR_NEWER
|
|
m_RenderTarget?.Release();
|
|
#endif
|
|
}
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
const string ProfilerTag = "GaussianSplatRenderGraph";
|
|
static readonly ProfilingSampler s_profilingSampler = new(ProfilerTag);
|
|
static readonly int s_gaussianSplatRT = Shader.PropertyToID(GaussianSplatRTName);
|
|
|
|
class PassData
|
|
{
|
|
internal UniversalCameraData CameraData;
|
|
internal TextureHandle SourceTexture;
|
|
internal TextureHandle SourceDepth;
|
|
internal TextureHandle GaussianSplatRT;
|
|
}
|
|
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
|
{
|
|
using var builder = renderGraph.AddUnsafePass(ProfilerTag, out PassData passData);
|
|
|
|
var cameraData = frameData.Get<UniversalCameraData>();
|
|
var resourceData = frameData.Get<UniversalResourceData>();
|
|
|
|
RenderTextureDescriptor rtDesc = cameraData.cameraTargetDescriptor;
|
|
rtDesc.depthBufferBits = 0;
|
|
rtDesc.msaaSamples = 1;
|
|
rtDesc.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
|
|
var textureHandle = UniversalRenderer.CreateRenderGraphTexture(renderGraph, rtDesc, GaussianSplatRTName, true);
|
|
|
|
passData.CameraData = cameraData;
|
|
passData.SourceTexture = resourceData.activeColorTexture;
|
|
passData.SourceDepth = resourceData.activeDepthTexture;
|
|
passData.GaussianSplatRT = textureHandle;
|
|
|
|
builder.UseTexture(resourceData.activeColorTexture, AccessFlags.ReadWrite);
|
|
builder.UseTexture(resourceData.activeDepthTexture);
|
|
builder.UseTexture(textureHandle, AccessFlags.Write);
|
|
builder.AllowPassCulling(false);
|
|
builder.SetRenderFunc(static (PassData data, UnsafeGraphContext context) =>
|
|
{
|
|
var commandBuffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
|
using var _ = new ProfilingScope(commandBuffer, s_profilingSampler);
|
|
commandBuffer.SetGlobalTexture(s_gaussianSplatRT, data.GaussianSplatRT);
|
|
CoreUtils.SetRenderTarget(commandBuffer, data.GaussianSplatRT, data.SourceDepth, ClearFlag.Color, Color.clear);
|
|
Material matComposite = GaussianSplatRenderSystem.instance.SortAndRenderSplats(data.CameraData.camera, commandBuffer);
|
|
commandBuffer.BeginSample(GaussianSplatRenderSystem.s_ProfCompose);
|
|
Blitter.BlitCameraTexture(commandBuffer, data.GaussianSplatRT, data.SourceTexture, matComposite, 0);
|
|
commandBuffer.EndSample(GaussianSplatRenderSystem.s_ProfCompose);
|
|
});
|
|
}
|
|
#else
|
|
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
|
{
|
|
RenderTextureDescriptor rtDesc = renderingData.cameraData.cameraTargetDescriptor;
|
|
rtDesc.depthBufferBits = 0;
|
|
rtDesc.msaaSamples = 1;
|
|
rtDesc.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
|
|
RenderingUtils.ReAllocateIfNeeded(ref m_RenderTarget, rtDesc, FilterMode.Point, TextureWrapMode.Clamp, name: GaussianSplatRTName);
|
|
cmd.SetGlobalTexture(m_RenderTarget.name, m_RenderTarget.nameID);
|
|
|
|
ConfigureTarget(m_RenderTarget, m_Renderer.cameraDepthTargetHandle);
|
|
ConfigureClear(ClearFlag.Color, new Color(0,0,0,0));
|
|
}
|
|
|
|
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
|
{
|
|
if (m_Cmb == null)
|
|
return;
|
|
|
|
// add sorting, view calc and drawing commands for each splat object
|
|
Material matComposite = GaussianSplatRenderSystem.instance.SortAndRenderSplats(renderingData.cameraData.camera, m_Cmb);
|
|
|
|
// compose
|
|
m_Cmb.BeginSample(GaussianSplatRenderSystem.s_ProfCompose);
|
|
Blitter.BlitCameraTexture(m_Cmb, m_RenderTarget, m_Renderer.cameraColorTargetHandle, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, matComposite, 0);
|
|
m_Cmb.EndSample(GaussianSplatRenderSystem.s_ProfCompose);
|
|
context.ExecuteCommandBuffer(m_Cmb);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
GSRenderPass m_Pass;
|
|
bool m_HasCamera;
|
|
|
|
public override void Create()
|
|
{
|
|
m_Pass = new GSRenderPass
|
|
{
|
|
renderPassEvent = RenderPassEvent.BeforeRenderingTransparents
|
|
};
|
|
}
|
|
|
|
public override void OnCameraPreCull(ScriptableRenderer renderer, in CameraData cameraData)
|
|
{
|
|
m_HasCamera = false;
|
|
var system = GaussianSplatRenderSystem.instance;
|
|
if (!system.GatherSplatsForCamera(cameraData.camera))
|
|
return;
|
|
|
|
#if !UNITY_6000_0_OR_NEWER
|
|
CommandBuffer cmb = system.InitialClearCmdBuffer(cameraData.camera);
|
|
m_Pass.m_Cmb = cmb;
|
|
#endif
|
|
m_HasCamera = true;
|
|
}
|
|
|
|
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
|
{
|
|
if (!m_HasCamera)
|
|
return;
|
|
#if !UNITY_6000_0_OR_NEWER
|
|
m_Pass.m_Renderer = renderer;
|
|
#endif
|
|
renderer.EnqueuePass(m_Pass);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
m_Pass?.Dispose();
|
|
m_Pass = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // #if GS_ENABLE_URP
|