You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
441 lines
22 KiB
441 lines
22 KiB
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); |
|
} |
|
} |
|
} |
|
}
|
|
|