Singularity/Library/PackageCache/com.unity.render-pipelines..../Runtime/RendererFeatures/ScreenSpaceAmbientOcclusion.cs

442 lines
22 KiB
C#
Raw Permalink Normal View History

2024-05-06 14:45:45 -04:00
using System;
#if UNITY_EDITOR
using ShaderKeywordFilter = UnityEditor.ShaderKeywordFilter;
#endif
namespace UnityEngine.Rendering.Universal
{
[Serializable]
internal class ScreenSpaceAmbientOcclusionSettings
{
// Parameters
[SerializeField] internal bool Downsample = false;
#if UNITY_EDITOR
// AfterOpaque requires also the "off variant" to be included
[ShaderKeywordFilter.SelectIf(true, overridePriority: true, keywordNames: new string[] {"", ShaderKeywordStrings.ScreenSpaceOcclusion})]
#endif
[SerializeField] internal bool AfterOpaque = false;
[SerializeField] internal DepthSource Source = DepthSource.DepthNormals;
[SerializeField] internal NormalQuality NormalSamples = NormalQuality.Medium;
[SerializeField] internal float Intensity = 3.0f;
[SerializeField] internal float DirectLightingStrength = 0.25f;
[SerializeField] internal float Radius = 0.035f;
[SerializeField] internal int SampleCount = 4;
// Enums
internal enum DepthSource
{
Depth = 0,
DepthNormals = 1
}
internal enum NormalQuality
{
Low,
Medium,
High
}
}
[DisallowMultipleRendererFeature]
[Tooltip("The Ambient Occlusion effect darkens creases, holes, intersections and surfaces that are close to each other.")]
internal class ScreenSpaceAmbientOcclusion : ScriptableRendererFeature
{
#if UNITY_EDITOR
[ShaderKeywordFilter.SelectIf(true, overridePriority: true, keywordNames: ShaderKeywordStrings.ScreenSpaceOcclusion)]
private const bool k_RequiresScreenSpaceOcclusion = true;
#endif
// Serialized Fields
[SerializeField, HideInInspector] private Shader m_Shader = null;
[SerializeField] private ScreenSpaceAmbientOcclusionSettings m_Settings = new ScreenSpaceAmbientOcclusionSettings();
// Private Fields
private Material m_Material;
private ScreenSpaceAmbientOcclusionPass m_SSAOPass = null;
// Constants
private const string k_ShaderName = "Hidden/Universal Render Pipeline/ScreenSpaceAmbientOcclusion";
private const string k_OrthographicCameraKeyword = "_ORTHOGRAPHIC";
private const string k_NormalReconstructionLowKeyword = "_RECONSTRUCT_NORMAL_LOW";
private const string k_NormalReconstructionMediumKeyword = "_RECONSTRUCT_NORMAL_MEDIUM";
private const string k_NormalReconstructionHighKeyword = "_RECONSTRUCT_NORMAL_HIGH";
private const string k_SourceDepthKeyword = "_SOURCE_DEPTH";
private const string k_SourceDepthNormalsKeyword = "_SOURCE_DEPTH_NORMALS";
internal bool afterOpaque => m_Settings.AfterOpaque;
/// <inheritdoc/>
public override void Create()
{
// Create the pass...
if (m_SSAOPass == null)
{
m_SSAOPass = new ScreenSpaceAmbientOcclusionPass();
}
GetMaterial();
}
/// <inheritdoc/>
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (!GetMaterial())
{
Debug.LogErrorFormat(
"{0}.AddRenderPasses(): Missing material. {1} render pass will not be added. Check for missing reference in the renderer resources.",
GetType().Name, name);
return;
}
bool shouldAdd = m_SSAOPass.Setup(m_Settings, renderer, m_Material);
if (shouldAdd)
{
renderer.EnqueuePass(m_SSAOPass);
}
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
CoreUtils.Destroy(m_Material);
}
private bool GetMaterial()
{
if (m_Material != null)
{
return true;
}
if (m_Shader == null)
{
m_Shader = Shader.Find(k_ShaderName);
if (m_Shader == null)
{
return false;
}
}
m_Material = CoreUtils.CreateEngineMaterial(m_Shader);
return m_Material != null;
}
// The SSAO Pass
private class ScreenSpaceAmbientOcclusionPass : ScriptableRenderPass
{
// Properties
private bool isRendererDeferred => m_Renderer != null && m_Renderer is UniversalRenderer && ((UniversalRenderer)m_Renderer).renderingMode == RenderingMode.Deferred;
// Private Variables
private bool m_SupportsR8RenderTextureFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8);
private Material m_Material;
private Vector4[] m_CameraTopLeftCorner = new Vector4[2];
private Vector4[] m_CameraXExtent = new Vector4[2];
private Vector4[] m_CameraYExtent = new Vector4[2];
private Vector4[] m_CameraZExtent = new Vector4[2];
private Matrix4x4[] m_CameraViewProjections = new Matrix4x4[2];
private ProfilingSampler m_ProfilingSampler = ProfilingSampler.Get(URPProfileId.SSAO);
private ScriptableRenderer m_Renderer = null;
private RenderTargetIdentifier m_SSAOTexture1Target = new RenderTargetIdentifier(s_SSAOTexture1ID, 0, CubemapFace.Unknown, -1);
private RenderTargetIdentifier m_SSAOTexture2Target = new RenderTargetIdentifier(s_SSAOTexture2ID, 0, CubemapFace.Unknown, -1);
private RenderTargetIdentifier m_SSAOTexture3Target = new RenderTargetIdentifier(s_SSAOTexture3ID, 0, CubemapFace.Unknown, -1);
private RenderTargetIdentifier m_SSAOTextureFinalTarget = new RenderTargetIdentifier(s_SSAOTextureFinalID, 0, CubemapFace.Unknown, -1);
private RenderTextureDescriptor m_AOPassDescriptor;
private RenderTextureDescriptor m_BlurPassesDescriptor;
private RenderTextureDescriptor m_FinalDescriptor;
private ScreenSpaceAmbientOcclusionSettings m_CurrentSettings;
// Constants
private const string k_SSAOTextureName = "_ScreenSpaceOcclusionTexture";
private const string k_SSAOAmbientOcclusionParamName = "_AmbientOcclusionParam";
// Statics
private static readonly int s_BaseMapID = Shader.PropertyToID("_BaseMap");
private static readonly int s_SSAOParamsID = Shader.PropertyToID("_SSAOParams");
private static readonly int s_SSAOTexture1ID = Shader.PropertyToID("_SSAO_OcclusionTexture1");
private static readonly int s_SSAOTexture2ID = Shader.PropertyToID("_SSAO_OcclusionTexture2");
private static readonly int s_SSAOTexture3ID = Shader.PropertyToID("_SSAO_OcclusionTexture3");
private static readonly int s_SSAOTextureFinalID = Shader.PropertyToID("_SSAO_OcclusionTexture");
private static readonly int s_CameraViewXExtentID = Shader.PropertyToID("_CameraViewXExtent");
private static readonly int s_CameraViewYExtentID = Shader.PropertyToID("_CameraViewYExtent");
private static readonly int s_CameraViewZExtentID = Shader.PropertyToID("_CameraViewZExtent");
private static readonly int s_ProjectionParams2ID = Shader.PropertyToID("_ProjectionParams2");
private static readonly int s_CameraViewProjectionsID = Shader.PropertyToID("_CameraViewProjections");
private static readonly int s_CameraViewTopLeftCornerID = Shader.PropertyToID("_CameraViewTopLeftCorner");
private enum ShaderPasses
{
AO = 0,
BlurHorizontal = 1,
BlurVertical = 2,
BlurFinal = 3,
AfterOpaque = 4
}
internal ScreenSpaceAmbientOcclusionPass()
{
m_CurrentSettings = new ScreenSpaceAmbientOcclusionSettings();
}
internal bool Setup(ScreenSpaceAmbientOcclusionSettings featureSettings, ScriptableRenderer renderer, Material material)
{
m_Material = material;
m_Renderer = renderer;
m_CurrentSettings = featureSettings;
ScreenSpaceAmbientOcclusionSettings.DepthSource source;
if (isRendererDeferred)
{
renderPassEvent = featureSettings.AfterOpaque ? RenderPassEvent.AfterRenderingOpaques : RenderPassEvent.AfterRenderingGbuffer;
source = ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
}
else
{
// Rendering after PrePasses is usually correct except when depth priming is in play:
// then we rely on a depth resolve taking place after the PrePasses in order to have it ready for SSAO.
// Hence we set the event to RenderPassEvent.AfterRenderingPrePasses + 1 at the earliest.
renderPassEvent = featureSettings.AfterOpaque ? RenderPassEvent.AfterRenderingOpaques : RenderPassEvent.AfterRenderingPrePasses + 1;
source = m_CurrentSettings.Source;
}
switch (source)
{
case ScreenSpaceAmbientOcclusionSettings.DepthSource.Depth:
ConfigureInput(ScriptableRenderPassInput.Depth);
break;
case ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals:
ConfigureInput(ScriptableRenderPassInput.Normal);// need depthNormal prepass for forward-only geometry
break;
default:
throw new ArgumentOutOfRangeException();
}
return m_Material != null
&& m_CurrentSettings.Intensity > 0.0f
&& m_CurrentSettings.Radius > 0.0f
&& m_CurrentSettings.SampleCount > 0;
}
/// <inheritdoc/>
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;
int downsampleDivider = m_CurrentSettings.Downsample ? 2 : 1;
// Update SSAO parameters in the material
Vector4 ssaoParams = new Vector4(
m_CurrentSettings.Intensity, // Intensity
m_CurrentSettings.Radius, // Radius
1.0f / downsampleDivider, // Downsampling
m_CurrentSettings.SampleCount // Sample count
);
m_Material.SetVector(s_SSAOParamsID, ssaoParams);
#if ENABLE_VR && ENABLE_XR_MODULE
int eyeCount = renderingData.cameraData.xr.enabled && renderingData.cameraData.xr.singlePassEnabled ? 2 : 1;
#else
int eyeCount = 1;
#endif
for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++)
{
Matrix4x4 view = renderingData.cameraData.GetViewMatrix(eyeIndex);
Matrix4x4 proj = renderingData.cameraData.GetProjectionMatrix(eyeIndex);
m_CameraViewProjections[eyeIndex] = proj * view;
// camera view space without translation, used by SSAO.hlsl ReconstructViewPos() to calculate view vector.
Matrix4x4 cview = view;
cview.SetColumn(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
Matrix4x4 cviewProj = proj * cview;
Matrix4x4 cviewProjInv = cviewProj.inverse;
Vector4 topLeftCorner = cviewProjInv.MultiplyPoint(new Vector4(-1, 1, -1, 1));
Vector4 topRightCorner = cviewProjInv.MultiplyPoint(new Vector4(1, 1, -1, 1));
Vector4 bottomLeftCorner = cviewProjInv.MultiplyPoint(new Vector4(-1, -1, -1, 1));
Vector4 farCentre = cviewProjInv.MultiplyPoint(new Vector4(0, 0, 1, 1));
m_CameraTopLeftCorner[eyeIndex] = topLeftCorner;
m_CameraXExtent[eyeIndex] = topRightCorner - topLeftCorner;
m_CameraYExtent[eyeIndex] = bottomLeftCorner - topLeftCorner;
m_CameraZExtent[eyeIndex] = farCentre;
}
m_Material.SetVector(s_ProjectionParams2ID, new Vector4(1.0f / renderingData.cameraData.camera.nearClipPlane, 0.0f, 0.0f, 0.0f));
m_Material.SetMatrixArray(s_CameraViewProjectionsID, m_CameraViewProjections);
m_Material.SetVectorArray(s_CameraViewTopLeftCornerID, m_CameraTopLeftCorner);
m_Material.SetVectorArray(s_CameraViewXExtentID, m_CameraXExtent);
m_Material.SetVectorArray(s_CameraViewYExtentID, m_CameraYExtent);
m_Material.SetVectorArray(s_CameraViewZExtentID, m_CameraZExtent);
// Update keywords
CoreUtils.SetKeyword(m_Material, k_OrthographicCameraKeyword, renderingData.cameraData.camera.orthographic);
ScreenSpaceAmbientOcclusionSettings.DepthSource source = this.isRendererDeferred
? ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals
: m_CurrentSettings.Source;
if (source == ScreenSpaceAmbientOcclusionSettings.DepthSource.Depth)
{
switch (m_CurrentSettings.NormalSamples)
{
case ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low:
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, true);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, false);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, false);
break;
case ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium:
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, false);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, true);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, false);
break;
case ScreenSpaceAmbientOcclusionSettings.NormalQuality.High:
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, false);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, false);
CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, true);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
switch (source)
{
case ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals:
CoreUtils.SetKeyword(m_Material, k_SourceDepthKeyword, false);
CoreUtils.SetKeyword(m_Material, k_SourceDepthNormalsKeyword, true);
break;
default:
CoreUtils.SetKeyword(m_Material, k_SourceDepthKeyword, true);
CoreUtils.SetKeyword(m_Material, k_SourceDepthNormalsKeyword, false);
break;
}
// Set up the descriptors
RenderTextureDescriptor descriptor = cameraTargetDescriptor;
descriptor.msaaSamples = 1;
descriptor.depthBufferBits = 0;
m_AOPassDescriptor = descriptor;
m_AOPassDescriptor.width /= downsampleDivider;
m_AOPassDescriptor.height /= downsampleDivider;
m_AOPassDescriptor.colorFormat = RenderTextureFormat.ARGB32;
m_BlurPassesDescriptor = descriptor;
m_BlurPassesDescriptor.colorFormat = RenderTextureFormat.ARGB32;
m_FinalDescriptor = descriptor;
m_FinalDescriptor.colorFormat = m_SupportsR8RenderTextureFormat ? RenderTextureFormat.R8 : RenderTextureFormat.ARGB32;
// Get temporary render textures
cmd.GetTemporaryRT(s_SSAOTexture1ID, m_AOPassDescriptor, FilterMode.Bilinear);
cmd.GetTemporaryRT(s_SSAOTexture2ID, m_BlurPassesDescriptor, FilterMode.Bilinear);
cmd.GetTemporaryRT(s_SSAOTexture3ID, m_BlurPassesDescriptor, FilterMode.Bilinear);
cmd.GetTemporaryRT(s_SSAOTextureFinalID, m_FinalDescriptor, FilterMode.Bilinear);
// Configure targets and clear color
ConfigureTarget(m_CurrentSettings.AfterOpaque ? m_Renderer.cameraColorTarget : s_SSAOTexture2ID);
ConfigureClear(ClearFlag.None, Color.white);
}
/// <inheritdoc/>
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (m_Material == null)
{
Debug.LogErrorFormat("{0}.Execute(): Missing material. ScreenSpaceAmbientOcclusion pass will not execute. Check for missing reference in the renderer resources.", GetType().Name);
return;
}
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_ProfilingSampler))
{
if (!m_CurrentSettings.AfterOpaque)
{
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ScreenSpaceOcclusion, true);
}
PostProcessUtils.SetSourceSize(cmd, m_AOPassDescriptor);
Vector4 scaleBiasRt = new Vector4(-1, 1.0f, -1.0f, 1.0f);
cmd.SetGlobalVector(Shader.PropertyToID("_ScaleBiasRt"), scaleBiasRt);
// Execute the SSAO
Render(cmd, m_SSAOTexture1Target, ShaderPasses.AO);
// Execute the Blur Passes
RenderAndSetBaseMap(cmd, m_SSAOTexture1Target, m_SSAOTexture2Target, ShaderPasses.BlurHorizontal);
PostProcessUtils.SetSourceSize(cmd, m_BlurPassesDescriptor);
RenderAndSetBaseMap(cmd, m_SSAOTexture2Target, m_SSAOTexture3Target, ShaderPasses.BlurVertical);
RenderAndSetBaseMap(cmd, m_SSAOTexture3Target, m_SSAOTextureFinalTarget, ShaderPasses.BlurFinal);
// Set the global SSAO texture and AO Params
cmd.SetGlobalTexture(k_SSAOTextureName, m_SSAOTextureFinalTarget);
cmd.SetGlobalVector(k_SSAOAmbientOcclusionParamName, new Vector4(0f, 0f, 0f, m_CurrentSettings.DirectLightingStrength));
// If true, SSAO pass is inserted after opaque pass and is expected to modulate lighting result now.
if (m_CurrentSettings.AfterOpaque)
{
// SetRenderTarget has logic to flip projection matrix when rendering to render texture. Flip the uv to account for that case.
CameraData cameraData = renderingData.cameraData;
bool isCameraColorFinalTarget = (cameraData.cameraType == CameraType.Game && m_Renderer.cameraColorTarget == BuiltinRenderTextureType.CameraTarget && cameraData.camera.targetTexture == null);
bool yflip = !isCameraColorFinalTarget;
float flipSign = yflip ? -1.0f : 1.0f;
scaleBiasRt = (flipSign < 0.0f)
? new Vector4(flipSign, 1.0f, -1.0f, 1.0f)
: new Vector4(flipSign, 0.0f, 1.0f, 1.0f);
cmd.SetGlobalVector(Shader.PropertyToID("_ScaleBiasRt"), scaleBiasRt);
// This implicitly also bind depth attachment. Explicitly binding m_Renderer.cameraDepthTarget does not work.
cmd.SetRenderTarget(
m_Renderer.cameraColorTarget,
RenderBufferLoadAction.Load,
RenderBufferStoreAction.Store
);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_Material, 0, (int)ShaderPasses.AfterOpaque);
}
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
private void Render(CommandBuffer cmd, RenderTargetIdentifier target, ShaderPasses pass)
{
cmd.SetRenderTarget(
target,
RenderBufferLoadAction.DontCare,
RenderBufferStoreAction.Store,
target,
RenderBufferLoadAction.DontCare,
RenderBufferStoreAction.DontCare
);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_Material, 0, (int)pass);
}
private void RenderAndSetBaseMap(CommandBuffer cmd, RenderTargetIdentifier baseMap, RenderTargetIdentifier target, ShaderPasses pass)
{
cmd.SetGlobalTexture(s_BaseMapID, baseMap);
Render(cmd, target, pass);
}
/// <inheritdoc/>
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
if (!m_CurrentSettings.AfterOpaque)
{
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ScreenSpaceOcclusion, false);
}
cmd.ReleaseTemporaryRT(s_SSAOTexture1ID);
cmd.ReleaseTemporaryRT(s_SSAOTexture2ID);
cmd.ReleaseTemporaryRT(s_SSAOTexture3ID);
cmd.ReleaseTemporaryRT(s_SSAOTextureFinalID);
}
}
}
}