using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
///
/// Render all objects that have a 'DepthNormals' and/or 'DepthNormalsOnly' pass into the given depth and normal buffers.
///
public class DepthNormalOnlyPass : ScriptableRenderPass
{
internal List shaderTagIds { get; set; }
private RTHandle depthHandle { get; set; }
private RTHandle normalHandle { get; set; }
private RTHandle renderingLayersHandle { get; set; }
internal bool enableRenderingLayers { get; set; } = false;
private FilteringSettings m_FilteringSettings;
private PassData m_PassData;
// Constants
private static readonly List k_DepthNormals = new List { new ShaderTagId("DepthNormals"), new ShaderTagId("DepthNormalsOnly") };
private static readonly RTHandle[] k_ColorAttachment1 = new RTHandle[1];
private static readonly RTHandle[] k_ColorAttachment2 = new RTHandle[2];
///
/// Creates a new DepthNormalOnlyPass instance.
///
/// The RenderPassEvent to use.
/// The RenderQueueRange to use for creating filtering settings that control what objects get rendered.
/// The layer mask to use for creating filtering settings that control what objects get rendered.
///
///
///
public DepthNormalOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask)
{
base.profilingSampler = new ProfilingSampler(nameof(DepthNormalOnlyPass));
m_PassData = new PassData();
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
renderPassEvent = evt;
useNativeRenderPass = false;
this.shaderTagIds = k_DepthNormals;
}
///
/// Finds the format to use for the normals texture.
///
/// The GraphicsFormat to use with the Normals texture.
public static GraphicsFormat GetGraphicsFormat()
{
if (RenderingUtils.SupportsGraphicsFormat(GraphicsFormat.R8G8B8A8_SNorm, FormatUsage.Render))
return GraphicsFormat.R8G8B8A8_SNorm; // Preferred format
else if (RenderingUtils.SupportsGraphicsFormat(GraphicsFormat.R16G16B16A16_SFloat, FormatUsage.Render))
return GraphicsFormat.R16G16B16A16_SFloat; // fallback
else
return GraphicsFormat.R32G32B32A32_SFloat; // fallback
}
///
/// Configures the pass.
///
/// The RTHandle used to render depth to.
/// The RTHandle used to render normals.
///
public void Setup(RTHandle depthHandle, RTHandle normalHandle)
{
this.depthHandle = depthHandle;
this.normalHandle = normalHandle;
this.enableRenderingLayers = false;
}
///
/// Configures the pass.
///
/// The RTHandle used to render depth to.
/// The RTHandle used to render normals.
/// The RTHandle used to render decals.
///
public void Setup(RTHandle depthHandle, RTHandle normalHandle, RTHandle decalLayerHandle)
{
Setup(depthHandle, normalHandle);
this.renderingLayersHandle = decalLayerHandle;
this.enableRenderingLayers = true;
}
///
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
RTHandle[] colorHandles;
if (this.enableRenderingLayers)
{
k_ColorAttachment2[0] = normalHandle;
k_ColorAttachment2[1] = renderingLayersHandle;
colorHandles = k_ColorAttachment2;
}
else
{
k_ColorAttachment1[0] = normalHandle;
colorHandles = k_ColorAttachment1;
}
if (renderingData.cameraData.renderer.useDepthPriming && (renderingData.cameraData.renderType == CameraRenderType.Base || renderingData.cameraData.clearDepth))
ConfigureTarget(colorHandles, renderingData.cameraData.renderer.cameraDepthTargetHandle);
else
ConfigureTarget(colorHandles, depthHandle);
ConfigureClear(ClearFlag.All, Color.black);
}
private static void ExecutePass(ScriptableRenderContext context, PassData passData, ref RenderingData renderingData)
{
var cmd = renderingData.commandBuffer;
var shaderTagIds = passData.shaderTagIds;
var filteringSettings = passData.filteringSettings;
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.DepthNormalPrepass)))
{
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
// Enable Rendering Layers
if (passData.enableRenderingLayers)
{
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, true);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
// Draw
var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
var drawSettings = RenderingUtils.CreateDrawingSettings(shaderTagIds, ref renderingData, sortFlags);
drawSettings.perObjectData = PerObjectData.None;
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filteringSettings);
// Clean up
if (passData.enableRenderingLayers)
{
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, false);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
}
}
///
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
m_PassData.shaderTagIds = this.shaderTagIds;
m_PassData.filteringSettings = m_FilteringSettings;
m_PassData.enableRenderingLayers = enableRenderingLayers;
ExecutePass(context, m_PassData, ref renderingData);
}
///
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
normalHandle = null;
depthHandle = null;
renderingLayersHandle = null;
}
private class PassData
{
internal TextureHandle cameraDepthTexture;
internal TextureHandle cameraNormalsTexture;
internal RenderingData renderingData;
internal List shaderTagIds;
internal FilteringSettings filteringSettings;
internal bool enableRenderingLayers;
}
internal void Render(RenderGraph renderGraph, out TextureHandle cameraNormalsTexture, out TextureHandle cameraDepthTexture, ref RenderingData renderingData)
{
const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
const int k_DepthBufferBits = 32;
using (var builder = renderGraph.AddRenderPass("DepthNormals Prepass", out var passData, base.profilingSampler))
{
var depthDescriptor = renderingData.cameraData.cameraTargetDescriptor;
depthDescriptor.graphicsFormat = GraphicsFormat.None;
depthDescriptor.depthStencilFormat = k_DepthStencilFormat;
depthDescriptor.depthBufferBits = k_DepthBufferBits;
depthDescriptor.msaaSamples = 1;// Depth-Only pass don't use MSAA
cameraDepthTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDescriptor, "_CameraDepthTexture", true);
// TODO RENDERGRAPH: Handle Deferred case, see _CameraNormalsTexture logic in UniversalRenderer.cs
var normalDescriptor = renderingData.cameraData.cameraTargetDescriptor;
normalDescriptor.depthBufferBits = 0;
// Never have MSAA on this depth texture. When doing MSAA depth priming this is the texture that is resolved to and used for post-processing.
normalDescriptor.msaaSamples = 1;// Depth-Only pass don't use MSAA
// Find compatible render-target format for storing normals.
// Shader code outputs normals in signed format to be compatible with deferred gbuffer layout.
// Deferred gbuffer format is signed so that normals can be blended for terrain geometry.
// TODO: deferred
normalDescriptor.graphicsFormat = GetGraphicsFormat();
cameraNormalsTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, normalDescriptor, "_CameraNormalsTexture", true);
passData.cameraNormalsTexture = builder.UseColorBuffer(cameraNormalsTexture, 0);
passData.cameraDepthTexture = builder.UseDepthBuffer(cameraDepthTexture, DepthAccess.Write);
passData.renderingData = renderingData;
passData.shaderTagIds = this.shaderTagIds;
passData.filteringSettings = m_FilteringSettings;
passData.enableRenderingLayers = enableRenderingLayers;
// TODO RENDERGRAPH: culling? force culling off for testing
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RenderGraphContext context) =>
{
ExecutePass(context.renderContext, data, ref data.renderingData);
});
return;
}
}
}
}