Files
XCEngine/MVS/3DGS-Unity/Runtime/GaussianSplatURPFeature.cs

159 lines
6.6 KiB
C#
Raw Normal View History

2026-03-29 01:36:53 +08:00
// 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