using UnityEngine.Rendering;
#if URP_7_OR_NEWER
using UnityEngine.Rendering.Universal;
using UnityEngine.XR.ARSubsystems;
using System;
#if URP_17_OR_NEWER
using UnityEngine.Rendering.RenderGraphModule;
#else
using UnityEngine.Experimental.Rendering;
#endif // URP_17_OR_NEWER
#else
using ScriptableRendererFeature = UnityEngine.ScriptableObject;
#endif // URP_7_OR_NEWER
namespace UnityEngine.XR.ARFoundation
{
///
/// A render feature for rendering the camera background for AR devices.
///
public class ARBackgroundRendererFeature : ScriptableRendererFeature
{
#if URP_7_OR_NEWER
///
/// The scriptable render pass to be added to the renderer when the camera background is to be rendered.
///
ARCameraBeforeOpaquesRenderPass beforeOpaquesScriptablePass => m_BeforeOpaquesScriptablePass ??= new ARCameraBeforeOpaquesRenderPass();
ARCameraBeforeOpaquesRenderPass m_BeforeOpaquesScriptablePass;
///
/// The scriptable render pass to be added to the renderer when the camera background is to be rendered.
///
ARCameraAfterOpaquesRenderPass afterOpaquesScriptablePass => m_AfterOpaquesScriptablePass ??= new ARCameraAfterOpaquesRenderPass();
ARCameraAfterOpaquesRenderPass m_AfterOpaquesScriptablePass;
///
/// Create the scriptable render pass.
///
public override void Create() { }
///
/// Add the background rendering pass when rendering a game camera with an enabled AR camera background component.
///
/// The scriptable renderer in which to enqueue the render pass.
/// Additional rendering data about the current state of rendering.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
var currentCamera = renderingData.cameraData.camera;
if (currentCamera != null && currentCamera.cameraType == CameraType.Game)
{
var cameraBackground = currentCamera.gameObject.GetComponent();
if (cameraBackground != null &&
cameraBackground.backgroundRenderingEnabled &&
cameraBackground.material != null &&
TrySelectRenderPassForBackgroundRenderMode(cameraBackground.currentRenderingMode, out var renderPass))
{
var invertCulling = cameraBackground.GetComponent()?.subsystem?.invertCulling ?? false;
renderPass.Setup(cameraBackground, invertCulling);
renderer.EnqueuePass(renderPass);
}
}
}
///
/// Selects the render pass for a given
///
/// The
/// that indicates which render pass to use.
///
/// The that corresponds
/// to the given .
///
///
/// true if was populated. Otherwise, false.
///
bool TrySelectRenderPassForBackgroundRenderMode(XRCameraBackgroundRenderingMode renderingMode, out ARCameraBackgroundRenderPass renderPass)
{
switch (renderingMode)
{
case XRCameraBackgroundRenderingMode.AfterOpaques:
renderPass = afterOpaquesScriptablePass;
return true;
case XRCameraBackgroundRenderingMode.BeforeOpaques:
renderPass = beforeOpaquesScriptablePass;
return true;
case XRCameraBackgroundRenderingMode.None:
default:
renderPass = null;
return false;
}
}
///
/// An abstract that provides common utilities for rendering an AR Camera Background.
///
abstract class ARCameraBackgroundRenderPass : ScriptableRenderPass
{
///
/// Data provided for the function.
///
class PassData
{
internal Matrix4x4 worldToCameraMatrix;
internal Matrix4x4 projectionMatrix;
internal bool invertCulling;
internal XRCameraBackgroundRenderingParams cameraBackgroundRenderingParams;
internal Material backgroundMaterial;
}
#if URP_17_OR_NEWER
// Name of our RenderGraph render pass.
const string k_RenderGraphPassName = "AR Background Render Pass (Render Graph Enabled)";
#endif // URP_17_OR_NEWER
///
/// The data that is passed to the render pass execute functions.
///
PassData m_RenderPassData = new();
///
/// The material used for rendering the device background using the camera video texture and potentially
/// other device-specific properties and textures.
///
Material m_BackgroundMaterial;
///
/// The geometry and transform of the camera background for a given platform.
///
XRCameraBackgroundRenderingParams m_CameraBackgroundRenderingParams;
ARDefaultCameraBackgroundRenderingParams m_DefaultCameraBackgroundRenderingParams;
///
/// Whether the culling mode should be inverted.
/// ([CommandBuffer.SetInvertCulling](https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.SetInvertCulling.html)).
///
bool m_InvertCulling;
///
/// The default platform geometry and transform for the camera background.
///
XRCameraBackgroundRenderingParams defaultCameraBackgroundRenderingParams
=> m_DefaultCameraBackgroundRenderingParams.SelectDefaultBackgroundRenderParametersForRenderMode(renderingMode);
///
/// The rendering mode for the camera background. Options are None, BeforeOpaques, and AfterOpaques.
///
protected abstract XRCameraBackgroundRenderingMode renderingMode { get; }
///
/// Set up the background render pass.
///
/// The ARCameraBackground component that provides the
/// and any additional rendering information required by the render pass.
/// Whether the culling mode should be inverted.
public void Setup(ARCameraBackground cameraBackground, bool invertCulling)
{
SetupInternal(cameraBackground);
m_DefaultCameraBackgroundRenderingParams = cameraBackground.defaultCameraBackgroundRenderingParams;
if (!cameraBackground.TryGetRenderingParameters(out m_CameraBackgroundRenderingParams))
m_CameraBackgroundRenderingParams = defaultCameraBackgroundRenderingParams;
m_BackgroundMaterial = cameraBackground.material;
m_InvertCulling = invertCulling;
}
///
/// Provides inheritors an opportunity to perform any specialized setup during
/// .
///
/// The ARCameraBackground component that provides the
/// and any additional rendering information required by the render pass.
protected virtual void SetupInternal(ARCameraBackground cameraBackground)
{
if (cameraBackground.occlusionManager != null && cameraBackground.occlusionManager.enabled)
{
// If an occlusion texture is being provided, rendering will need
// to compare it against the depth texture created by the camera.
ConfigureInput(ScriptableRenderPassInput.Depth);
}
}
///
/// Execute the commands to render the camera background with RenderGraph disabled.
///
/// The render context for executing the render commands.
/// Additional rendering data about the current state of rendering.
#pragma warning disable CS0672
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
#pragma warning restore CS0672
{
// Populate struct to send to ExecuteRenderPass
m_RenderPassData.worldToCameraMatrix = renderingData.cameraData.camera.worldToCameraMatrix;
m_RenderPassData.projectionMatrix = renderingData.cameraData.camera.projectionMatrix;
m_RenderPassData.invertCulling = m_InvertCulling;
m_RenderPassData.cameraBackgroundRenderingParams = m_CameraBackgroundRenderingParams;
m_RenderPassData.backgroundMaterial = m_BackgroundMaterial;
var cmd = CommandBufferPool.Get("AR Background Render Pass (Render Graph Disabled)");
ExecuteRenderPass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), m_RenderPassData);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
///
/// Execute the commands to render the camera background. This function is used for both RenderGraph and
/// non-RenderGraph paths.
///
/// The RasterCommandBuffer object that allows us to enqueue
/// rendering instructions to the native for this render pass.
/// The data that is passed to the function that executes this render pass.
static void ExecuteRenderPass(RasterCommandBuffer rasterCommandBuffer, PassData passData)
{
ARCameraBackground.AddBeforeBackgroundRenderHandler(rasterCommandBuffer);
rasterCommandBuffer.SetInvertCulling(passData.invertCulling);
rasterCommandBuffer.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
rasterCommandBuffer.DrawMesh(
passData.cameraBackgroundRenderingParams.backgroundGeometry,
passData.cameraBackgroundRenderingParams.backgroundTransform,
passData.backgroundMaterial);
rasterCommandBuffer.SetViewProjectionMatrices(
passData.worldToCameraMatrix,
passData.projectionMatrix);
}
#if URP_17_OR_NEWER
///
/// This is part of the RenderGraph path. It calls , which is shared by both the
/// RenderGraph and non-RenderGraph paths.
///
/// The data that is passed to the function that executes this render pass.
/// The RasterGraphContext object that allows us to access the
/// and native to enqueue rendering instructions
/// for this render pass.
static void ExecuteRasterRenderGraphPass(PassData passData, RasterGraphContext rasterContext)
{
ExecuteRenderPass(rasterContext.cmd, passData);
}
///
/// Create and add the raster pass to RenderGraph, as well as storing all the data needed to execute the pass.
///
/// The RenderGraph object that we add the raster render pass to.
/// A ContextContainer object that gives us access to the camera and render
/// texture data for this RenderGraph raster pass.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
using (var builder = renderGraph.AddRasterRenderPass(
k_RenderGraphPassName,
out m_RenderPassData,
profilingSampler))
{
UniversalCameraData cameraData = frameData.Get();
UniversalResourceData resourceData = frameData.Get();
// Populate struct to send to ExecuteRasterRenderGraphPass
m_RenderPassData.worldToCameraMatrix = cameraData.camera.worldToCameraMatrix;
m_RenderPassData.projectionMatrix = cameraData.camera.projectionMatrix;
m_RenderPassData.invertCulling = m_InvertCulling;
m_RenderPassData.cameraBackgroundRenderingParams = m_CameraBackgroundRenderingParams;
m_RenderPassData.backgroundMaterial = m_BackgroundMaterial;
// Shader keyword changes are considered global state modifications
builder.AllowGlobalStateModification(true);
builder.AllowPassCulling(false);
// The RenderGraph render target is the main camera's active color TextureHandle
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
// The RenderGraph depth target is the main camera's active depth TextureHandle
builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture);
builder.SetRenderFunc(ExecuteRasterRenderGraphPass);
}
}
#endif // URP_17_OR_NEWER
///
/// Called upon finish rendering a camera. Releases any resources created by this render pass or
/// otherwise executes any cleanup code.
///
/// Use this CommandBuffer to cleanup any generated data
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException(nameof(cmd));
// If invert culling is true, we must make sure it is only enabled for the minimum amount of time
// in code where it is needed, since other systems won't necessarily work when it is enabled.
if (m_InvertCulling)
cmd.SetInvertCulling(false);
}
}
///
/// The custom render pass to render the camera background before rendering opaques.
///
class ARCameraBeforeOpaquesRenderPass : ARCameraBackgroundRenderPass
{
///
/// Constructs the background render pass.
///
public ARCameraBeforeOpaquesRenderPass()
{
renderPassEvent = RenderPassEvent.BeforeRenderingOpaques;
}
///
/// Configure the render pass by setting the render target and clear values.
///
/// The command buffer for configuration.
/// The descriptor of the target render texture.
#pragma warning disable CS0672
public override void Configure(CommandBuffer commandBuffer, RenderTextureDescriptor renderTextureDescriptor)
#pragma warning restore CS0672
{
#pragma warning disable CS0618
ConfigureClear(ClearFlag.Depth, Color.clear);
#pragma warning restore CS0618
}
protected override XRCameraBackgroundRenderingMode renderingMode
=> XRCameraBackgroundRenderingMode.BeforeOpaques;
}
///
/// The custom render pass to render the camera background after rendering opaques.
///
class ARCameraAfterOpaquesRenderPass : ARCameraBackgroundRenderPass
{
///
/// Constructs the background render pass.
///
public ARCameraAfterOpaquesRenderPass()
{
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
}
///
/// Configure the render pass by setting the render target and clear values.
///
/// The command buffer for configuration.
/// The descriptor of the target render texture.
#pragma warning disable CS0672
public override void Configure(CommandBuffer commandBuffer, RenderTextureDescriptor renderTextureDescriptor)
#pragma warning restore CS0672
{
#pragma warning disable CS0618
ConfigureClear(ClearFlag.None, Color.clear);
#pragma warning restore CS0618
}
protected override XRCameraBackgroundRenderingMode renderingMode
=> XRCameraBackgroundRenderingMode.AfterOpaques;
}
#endif // URP_7_OR_NEWER
}
}