616 lines
30 KiB
C#
616 lines
30 KiB
C#
using System;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
namespace UnityEngine.Rendering.Universal
|
|
{
|
|
internal static class SSAOUtils
|
|
{
|
|
// Constants
|
|
internal const string k_SSAOTextureName = "_ScreenSpaceOcclusionTexture";
|
|
|
|
// Shared Shader Property IDs
|
|
internal static class ShaderConstants
|
|
{
|
|
public static readonly int _AmbientOcclusionParam = Shader.PropertyToID("_AmbientOcclusionParam");
|
|
public static readonly int _SSAOParams = Shader.PropertyToID("_SSAOParams");
|
|
public static readonly int _SSAOBlueNoiseParams = Shader.PropertyToID("_SSAOBlueNoiseParams");
|
|
public static readonly int _BlueNoiseTexture = Shader.PropertyToID("_BlueNoiseTexture");
|
|
public static readonly int _SSAOFinalTexture = Shader.PropertyToID(k_SSAOTextureName);
|
|
public static readonly int _CameraViewXExtent = Shader.PropertyToID("_CameraViewXExtent");
|
|
public static readonly int _CameraViewYExtent = Shader.PropertyToID("_CameraViewYExtent");
|
|
public static readonly int _CameraViewZExtent = Shader.PropertyToID("_CameraViewZExtent");
|
|
public static readonly int _ProjectionParams2 = Shader.PropertyToID("_ProjectionParams2");
|
|
public static readonly int _CameraViewProjections = Shader.PropertyToID("_CameraViewProjections");
|
|
public static readonly int _CameraViewTopLeftCorner = Shader.PropertyToID("_CameraViewTopLeftCorner");
|
|
public static readonly int _CameraNormalsTexture = Shader.PropertyToID("_CameraNormalsTexture");
|
|
public static readonly int _BlitScaleBias = Shader.PropertyToID("_BlitScaleBias");
|
|
public static readonly int _BlitTexture = Shader.PropertyToID("_BlitTexture");
|
|
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
|
|
|
|
#if MODERN_SSAO
|
|
public static readonly int _SSAOParams2 = Shader.PropertyToID("_SSAOParams2");
|
|
public static readonly int _AODepthToViewParams = Shader.PropertyToID("_AODepthToViewParams");
|
|
#endif
|
|
}
|
|
|
|
// Enums
|
|
internal enum BlurTypes
|
|
{
|
|
Bilateral,
|
|
Gaussian,
|
|
Kawase,
|
|
}
|
|
|
|
internal enum ShaderPasses
|
|
{
|
|
AmbientOcclusion = 0,
|
|
|
|
BilateralBlurHorizontal = 1,
|
|
BilateralBlurVertical = 2,
|
|
BilateralBlurFinal = 3,
|
|
BilateralAfterOpaque = 4,
|
|
|
|
GaussianBlurHorizontal = 5,
|
|
GaussianBlurVertical = 6,
|
|
GaussianAfterOpaque = 7,
|
|
|
|
KawaseBlur = 8,
|
|
KawaseAfterOpaque = 9,
|
|
|
|
BoxAfterOpaque = 10,
|
|
}
|
|
|
|
// Camera view data aggregated into a struct to avoid scattered arrays.
|
|
internal struct CameraViewData
|
|
{
|
|
public Vector4[] topLeftCorner;
|
|
public Vector4[] xExtent;
|
|
public Vector4[] yExtent;
|
|
public Vector4[] zExtent;
|
|
public Matrix4x4[] viewProjections;
|
|
|
|
public static CameraViewData Create()
|
|
{
|
|
return new CameraViewData
|
|
{
|
|
topLeftCorner = new Vector4[2],
|
|
xExtent = new Vector4[2],
|
|
yExtent = new Vector4[2],
|
|
zExtent = new Vector4[2],
|
|
viewProjections = new Matrix4x4[2],
|
|
};
|
|
}
|
|
}
|
|
|
|
// Structs
|
|
internal readonly struct SSAOMaterialParams
|
|
{
|
|
internal readonly bool orthographicCamera;
|
|
internal readonly bool aoBlueNoise;
|
|
internal readonly bool aoInterleavedGradient;
|
|
internal readonly bool sampleCountHigh;
|
|
internal readonly bool sampleCountMedium;
|
|
internal readonly bool sampleCountLow;
|
|
internal readonly bool sourceDepthNormals;
|
|
internal readonly bool sourceDepthHigh;
|
|
internal readonly bool sourceDepthMedium;
|
|
internal readonly bool sourceDepthLow;
|
|
internal readonly Vector4 ssaoParams;
|
|
#if MODERN_SSAO
|
|
internal readonly Vector4 ssaoParams2;
|
|
internal readonly Vector4 depthToViewParams;
|
|
internal readonly bool isGTAOMode;
|
|
#endif
|
|
|
|
internal SSAOMaterialParams(ScreenSpaceAmbientOcclusionSettings settings, UniversalCameraData cameraData, in TextureDesc cameraColorDesc)
|
|
{
|
|
float radius = CalculateRadius(settings);
|
|
orthographicCamera = cameraData.camera.orthographic;
|
|
sampleCountHigh = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.High;
|
|
sampleCountMedium = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Medium;
|
|
sampleCountLow = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Low;
|
|
|
|
#if MODERN_SSAO
|
|
isGTAOMode = settings.Mode != ScreenSpaceAmbientOcclusionMode.Standard;
|
|
|
|
if (isGTAOMode)
|
|
{
|
|
// GTAO always uses depth normals
|
|
sourceDepthNormals = true;
|
|
|
|
aoBlueNoise = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
|
|
aoInterleavedGradient = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
|
|
sourceDepthHigh = false;
|
|
sourceDepthMedium = false;
|
|
sourceDepthLow = false;
|
|
|
|
GTAOPass.CalculateGTAOViewParams(settings, cameraData, orthographicCamera, radius, cameraColorDesc, out ssaoParams2, out depthToViewParams);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
// Standard mode
|
|
bool isUsingDepthNormals = settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
|
|
aoBlueNoise = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
|
|
aoInterleavedGradient = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
|
|
sourceDepthNormals = settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
|
|
sourceDepthHigh = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.High;
|
|
sourceDepthMedium = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium;
|
|
sourceDepthLow = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low;
|
|
#if MODERN_SSAO
|
|
ssaoParams2 = Vector4.zero;
|
|
depthToViewParams = Vector4.zero;
|
|
}
|
|
#endif
|
|
ssaoParams = CalculateCommonParams(settings, radius);
|
|
}
|
|
|
|
internal bool Equals(in SSAOMaterialParams other)
|
|
{
|
|
return orthographicCamera == other.orthographicCamera
|
|
&& aoBlueNoise == other.aoBlueNoise
|
|
&& aoInterleavedGradient == other.aoInterleavedGradient
|
|
&& sampleCountHigh == other.sampleCountHigh
|
|
&& sampleCountMedium == other.sampleCountMedium
|
|
&& sampleCountLow == other.sampleCountLow
|
|
&& sourceDepthNormals == other.sourceDepthNormals
|
|
&& sourceDepthHigh == other.sourceDepthHigh
|
|
&& sourceDepthMedium == other.sourceDepthMedium
|
|
&& sourceDepthLow == other.sourceDepthLow
|
|
&& ssaoParams == other.ssaoParams
|
|
#if MODERN_SSAO
|
|
&& ssaoParams2 == other.ssaoParams2
|
|
&& depthToViewParams == other.depthToViewParams
|
|
&& isGTAOMode == other.isGTAOMode
|
|
#endif
|
|
;
|
|
}
|
|
}
|
|
|
|
// ---- Static Utility Methods ----
|
|
|
|
internal static float CalculateRadius(ScreenSpaceAmbientOcclusionSettings settings)
|
|
{
|
|
float radiusMultiplier = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise ? 1.5f : 1.0f;
|
|
return settings.Radius * radiusMultiplier;
|
|
}
|
|
|
|
internal static Vector4 CalculateCommonParams(ScreenSpaceAmbientOcclusionSettings settings, float radius)
|
|
{
|
|
return new Vector4(settings.Intensity, radius, 1.0f / (settings.Downsample ? 2 : 1), settings.Falloff);
|
|
}
|
|
|
|
internal static Vector4 CalculateProjectionParams2(UniversalCameraData cameraData)
|
|
{
|
|
return new Vector4(1.0f / cameraData.camera.nearClipPlane, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
internal static Vector2 GetR2Offset()
|
|
{
|
|
// R2 sequence from https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
|
|
// x_n = (n * (1/phi_2)) mod 1, y_n = (n * (1/phi_2^2)) mod 1
|
|
//
|
|
// Use uint multiplication so wrap-around handles "mod 1" exactly,
|
|
// regardless of how large Time.frameCount grows. Constants are the
|
|
// irrational R2 ratios scaled by 2^32 and rounded:
|
|
// round(2^32 * 0.7548776662466927) = 3242174889
|
|
// round(2^32 * 0.5698402909980532) = 2447445413
|
|
const uint k_R2SequenceX = 3242174889u;
|
|
const uint k_R2SequenceY = 2447445413u;
|
|
const float k_InvScale = 1.0f / 4294967296.0f; // 1 / 2^32
|
|
|
|
uint sampleIndex = (uint)(Time.frameCount + 1);
|
|
uint offsetX = sampleIndex * k_R2SequenceX; // wraps mod 2^32 ⇔ mod 1
|
|
uint offsetY = sampleIndex * k_R2SequenceY;
|
|
return new Vector2(offsetX * k_InvScale, offsetY * k_InvScale);
|
|
}
|
|
|
|
internal static Vector4 CalculateBlueNoiseParams(UniversalCameraData cameraData, Texture2D noiseTexture)
|
|
{
|
|
#if UNITY_INCLUDE_TESTS
|
|
return new Vector4(
|
|
cameraData.pixelWidth / (float)noiseTexture.width,
|
|
cameraData.pixelHeight / (float)noiseTexture.height,
|
|
1.0f,
|
|
1.0f);
|
|
#else
|
|
Vector2 blueNoiseOffset = GetR2Offset();
|
|
return new Vector4(
|
|
cameraData.pixelWidth / (float)noiseTexture.width,
|
|
cameraData.pixelHeight / (float)noiseTexture.height,
|
|
blueNoiseOffset.x,
|
|
blueNoiseOffset.y);
|
|
#endif
|
|
}
|
|
|
|
internal static Texture2D GetBlueNoiseTexture(Texture2D[] blueNoiseTextures, int index)
|
|
{
|
|
if (blueNoiseTextures == null || blueNoiseTextures.Length == 0)
|
|
return null;
|
|
|
|
return blueNoiseTextures[index % blueNoiseTextures.Length];
|
|
}
|
|
|
|
internal static int AdvanceBlueNoiseIndex(ScreenSpaceAmbientOcclusionSettings settings, Texture2D[] blueNoiseTextures, int currentIndex)
|
|
{
|
|
if (settings.AOMethod != ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise ||
|
|
blueNoiseTextures == null || blueNoiseTextures.Length == 0)
|
|
{
|
|
return currentIndex;
|
|
}
|
|
|
|
#if UNITY_INCLUDE_TESTS
|
|
return 0;
|
|
#else
|
|
return (currentIndex + 1) % blueNoiseTextures.Length;
|
|
#endif
|
|
}
|
|
|
|
internal static BlurTypes GetBlurType(ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions quality)
|
|
{
|
|
return quality switch
|
|
{
|
|
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High => BlurTypes.Bilateral,
|
|
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Medium => BlurTypes.Gaussian,
|
|
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low => BlurTypes.Kawase,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(quality))
|
|
};
|
|
}
|
|
|
|
internal static void SetupCameraViewMatrices(UniversalCameraData cameraData, ref CameraViewData viewData)
|
|
{
|
|
#if ENABLE_VR && ENABLE_XR_MODULE
|
|
int eyeCount = cameraData.xr.enabled && cameraData.xr.singlePassEnabled ? 2 : 1;
|
|
#else
|
|
int eyeCount = 1;
|
|
#endif
|
|
|
|
for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++)
|
|
{
|
|
Matrix4x4 view = cameraData.GetViewMatrix(eyeIndex);
|
|
Matrix4x4 proj = cameraData.GetProjectionMatrix(eyeIndex);
|
|
viewData.viewProjections[eyeIndex] = proj * view;
|
|
|
|
// camera view space without translation, used by SSAO.hlsl ReconstructViewPos() to calculate view vector.
|
|
Matrix4x4 camView = view;
|
|
camView.SetColumn(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
|
|
Matrix4x4 camViewProj = proj * camView;
|
|
Matrix4x4 camViewProjInv = camViewProj.inverse;
|
|
|
|
Vector4 topLeftCorner = camViewProjInv.MultiplyPoint(new Vector4(-1, 1, -1, 1));
|
|
Vector4 topRightCorner = camViewProjInv.MultiplyPoint(new Vector4(1, 1, -1, 1));
|
|
Vector4 bottomLeftCorner = camViewProjInv.MultiplyPoint(new Vector4(-1, -1, -1, 1));
|
|
Vector4 farCentre = camViewProjInv.MultiplyPoint(new Vector4(0, 0, 1, 1));
|
|
viewData.topLeftCorner[eyeIndex] = topLeftCorner;
|
|
viewData.xExtent[eyeIndex] = topRightCorner - topLeftCorner;
|
|
viewData.yExtent[eyeIndex] = bottomLeftCorner - topLeftCorner;
|
|
viewData.zExtent[eyeIndex] = farCentre;
|
|
}
|
|
}
|
|
|
|
internal static bool IsYFlip(RasterGraphContext ctx, in TextureHandle srcTexture, in TextureHandle dstTexture)
|
|
{
|
|
return ctx.GetTextureUVOrigin(srcTexture) != ctx.GetTextureUVOrigin(dstTexture);
|
|
}
|
|
|
|
internal static Vector4 ComputeScaleBias(in TextureHandle source, bool yFlip)
|
|
{
|
|
RTHandle srcRTHandle = source;
|
|
Vector2 viewportScale;
|
|
if (srcRTHandle is { useScaling: true })
|
|
{
|
|
var scale = srcRTHandle.rtHandleProperties.rtHandleScale;
|
|
viewportScale.x = scale.x;
|
|
viewportScale.y = scale.y;
|
|
}
|
|
else
|
|
{
|
|
viewportScale = Vector2.one;
|
|
}
|
|
|
|
if (yFlip)
|
|
return new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y);
|
|
else
|
|
return new Vector4(viewportScale.x, viewportScale.y, 0, 0);
|
|
}
|
|
|
|
// ---- Shared Raster Recording Methods ----
|
|
|
|
internal static void SetupKeywordsAndParameters(
|
|
Material material,
|
|
ScreenSpaceAmbientOcclusionSettings settings,
|
|
UniversalCameraData cameraData,
|
|
in TextureDesc cameraColorDesc,
|
|
ref CameraViewData viewData,
|
|
Texture2D[] blueNoiseTextures,
|
|
int blueNoiseTextureIndex,
|
|
ref SSAOMaterialParams prevParams)
|
|
{
|
|
material.SetVector(ShaderConstants._ProjectionParams2, CalculateProjectionParams2(cameraData));
|
|
material.SetMatrixArray(ShaderConstants._CameraViewProjections, viewData.viewProjections);
|
|
material.SetVectorArray(ShaderConstants._CameraViewTopLeftCorner, viewData.topLeftCorner);
|
|
material.SetVectorArray(ShaderConstants._CameraViewXExtent, viewData.xExtent);
|
|
material.SetVectorArray(ShaderConstants._CameraViewYExtent, viewData.yExtent);
|
|
material.SetVectorArray(ShaderConstants._CameraViewZExtent, viewData.zExtent);
|
|
|
|
if (settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise)
|
|
{
|
|
Texture2D noiseTexture = GetBlueNoiseTexture(blueNoiseTextures, blueNoiseTextureIndex);
|
|
Debug.Assert(noiseTexture != null, "Blue noise texture is null. Blue noise mode requires a valid blue noise texture.");
|
|
Vector4 blueNoiseParams = CalculateBlueNoiseParams(cameraData, noiseTexture);
|
|
|
|
material.SetTexture(ShaderConstants._BlueNoiseTexture, noiseTexture);
|
|
material.SetVector(ShaderConstants._SSAOBlueNoiseParams, blueNoiseParams);
|
|
}
|
|
else
|
|
{
|
|
material.SetVector(ShaderConstants._SSAOBlueNoiseParams, Vector4.zero);
|
|
}
|
|
|
|
// Setting keywords can be somewhat expensive on low-end platforms.
|
|
// Previous params are cached to avoid setting the same keywords every frame.
|
|
SSAOMaterialParams matParams = new SSAOMaterialParams(settings, cameraData, cameraColorDesc);
|
|
bool ssaoParamsDirty = !prevParams.Equals(in matParams);
|
|
bool isParamsPropertySet = material.HasProperty(ShaderConstants._SSAOParams);
|
|
if (!ssaoParamsDirty && isParamsPropertySet)
|
|
return;
|
|
|
|
prevParams = matParams;
|
|
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountHighKeyword, matParams.sampleCountHigh);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountMediumKeyword, matParams.sampleCountMedium);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountLowKeyword, matParams.sampleCountLow);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthNormalsKeyword, matParams.sourceDepthNormals);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthHighKeyword, matParams.sourceDepthHigh);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthMediumKeyword, matParams.sourceDepthMedium);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthLowKeyword, matParams.sourceDepthLow);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_OrthographicCameraKeyword, matParams.orthographicCamera);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_AOBlueNoiseKeyword, matParams.aoBlueNoise);
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_AOInterleavedGradientKeyword, matParams.aoInterleavedGradient);
|
|
material.SetVector(ShaderConstants._SSAOParams, matParams.ssaoParams);
|
|
#if MODERN_SSAO
|
|
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_GTAOModeKeyword, matParams.isGTAOMode);
|
|
if (matParams.isGTAOMode)
|
|
{
|
|
material.SetVector(ShaderConstants._SSAOParams2, matParams.ssaoParams2);
|
|
material.SetVector(ShaderConstants._AODepthToViewParams, matParams.depthToViewParams);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Pass data classes for shared raster recording
|
|
internal class RasterAOPassData
|
|
{
|
|
internal bool afterOpaque;
|
|
internal ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions blurQuality;
|
|
internal Material material;
|
|
internal float directLightingStrength;
|
|
internal Vector4 sourceSize;
|
|
internal TextureHandle aoTexture;
|
|
internal TextureHandle finalTexture;
|
|
internal TextureHandle blurTexture;
|
|
internal TextureHandle cameraNormalsTexture;
|
|
}
|
|
|
|
internal class BlurPassData
|
|
{
|
|
internal TextureHandle srcTexture;
|
|
internal TextureHandle dstTexture;
|
|
internal Material material;
|
|
internal UniversalCameraData cameraData;
|
|
internal int pass;
|
|
internal ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions blurQuality;
|
|
internal bool afterOpaque;
|
|
}
|
|
|
|
internal class CleanupPassData
|
|
{
|
|
internal float directLightingStrength;
|
|
}
|
|
|
|
internal static void RecordRasterAOPass(
|
|
RenderGraph renderGraph,
|
|
ProfilingSampler profilingSampler,
|
|
Material material,
|
|
ScreenSpaceAmbientOcclusionSettings settings,
|
|
TextureHandle aoTexture,
|
|
TextureHandle cameraDepthTexture,
|
|
TextureHandle cameraNormalsTexture,
|
|
in TextureDesc cameraColorDesc)
|
|
{
|
|
using (var builder = renderGraph.AddRasterRenderPass<RasterAOPassData>("Blit SSAO", out var passData, profilingSampler))
|
|
{
|
|
builder.AllowGlobalStateModification(true);
|
|
|
|
passData.material = material;
|
|
passData.blurQuality = settings.BlurQuality;
|
|
passData.afterOpaque = settings.AfterOpaque;
|
|
passData.directLightingStrength = settings.DirectLightingStrength;
|
|
passData.sourceSize = PostProcessUtils.CalcShaderSourceSize(cameraColorDesc.width, cameraColorDesc.height, cameraColorDesc.useDynamicScale);
|
|
passData.aoTexture = aoTexture;
|
|
|
|
builder.SetRenderAttachment(passData.aoTexture, 0, AccessFlags.WriteAll);
|
|
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
|
|
|
|
if (settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals)
|
|
{
|
|
builder.UseTexture(cameraNormalsTexture, AccessFlags.Read);
|
|
passData.cameraNormalsTexture = cameraNormalsTexture;
|
|
}
|
|
|
|
builder.SetRenderFunc(static (RasterAOPassData data, RasterGraphContext ctx) =>
|
|
{
|
|
ctx.cmd.SetGlobalVector(ShaderConstants._SourceSize, data.sourceSize);
|
|
|
|
if (data.cameraNormalsTexture.IsValid())
|
|
data.material.SetTexture(ShaderConstants._CameraNormalsTexture, data.cameraNormalsTexture);
|
|
|
|
Vector4 viewScaleBias = new(1, 1, 0, 0);
|
|
Blitter.BlitTexture(ctx.cmd, viewScaleBias, data.material, (int)ShaderPasses.AmbientOcclusion);
|
|
});
|
|
}
|
|
}
|
|
|
|
internal static void RecordBlurStep(
|
|
RenderGraph renderGraph,
|
|
ProfilingSampler profilingSampler,
|
|
Material material,
|
|
ScreenSpaceAmbientOcclusionSettings settings,
|
|
UniversalCameraData cameraData,
|
|
string blurPassName,
|
|
in TextureHandle src,
|
|
in TextureHandle dst,
|
|
int pass,
|
|
bool isLastPass)
|
|
{
|
|
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>(blurPassName, out var passData, profilingSampler))
|
|
{
|
|
passData.material = material;
|
|
passData.blurQuality = settings.BlurQuality;
|
|
passData.afterOpaque = settings.AfterOpaque;
|
|
passData.srcTexture = src;
|
|
passData.dstTexture = dst;
|
|
passData.cameraData = cameraData;
|
|
passData.pass = pass;
|
|
|
|
builder.UseTexture(passData.srcTexture);
|
|
|
|
AccessFlags finalDstAccess = passData.afterOpaque && isLastPass ? AccessFlags.Write : AccessFlags.WriteAll;
|
|
builder.SetRenderAttachment(passData.dstTexture, 0, finalDstAccess);
|
|
|
|
builder.SetRenderFunc(static (BlurPassData data, RasterGraphContext ctx) =>
|
|
{
|
|
Vector4 viewScaleBias = ComputeScaleBias(data.srcTexture, IsYFlip(ctx, in data.srcTexture, in data.dstTexture));
|
|
Blitter.BlitTexture(ctx.cmd, data.srcTexture, viewScaleBias, data.material, data.pass);
|
|
});
|
|
}
|
|
}
|
|
|
|
internal static void RecordBlurChain(
|
|
RenderGraph renderGraph,
|
|
ProfilingSampler profilingSampler,
|
|
Material material,
|
|
ScreenSpaceAmbientOcclusionSettings settings,
|
|
UniversalCameraData cameraData,
|
|
TextureHandle aoTexture,
|
|
TextureHandle blurTexture,
|
|
TextureHandle finalTexture)
|
|
{
|
|
switch (settings.BlurQuality)
|
|
{
|
|
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High:
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Horizontal (High)", aoTexture, blurTexture, (int)ShaderPasses.BilateralBlurHorizontal, false);
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Vertical (High)", blurTexture, aoTexture, (int)ShaderPasses.BilateralBlurVertical, false);
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Final (High)", aoTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.BilateralAfterOpaque : ShaderPasses.BilateralBlurFinal), true);
|
|
break;
|
|
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Medium:
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Horizontal (Medium)", aoTexture, blurTexture, (int)ShaderPasses.GaussianBlurHorizontal, false);
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Final (Medium)", blurTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.GaussianAfterOpaque : ShaderPasses.GaussianBlurVertical), true);
|
|
break;
|
|
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low:
|
|
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO (Low)", aoTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.KawaseAfterOpaque : ShaderPasses.KawaseBlur), true);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
|
|
internal static void RecordCleanupPass(
|
|
RenderGraph renderGraph,
|
|
ProfilingSampler profilingSampler,
|
|
float directLightingStrength,
|
|
TextureHandle finalTexture)
|
|
{
|
|
// Add cleanup pass to:
|
|
// - Set global keywords for next passes
|
|
// - Set global texture as there is a limitation in Render Graph where an input texture cannot be set as a global texture after the pass runs
|
|
// A Raster pass is used so it can be merged easily with the blur passes.
|
|
using (var builder = renderGraph.AddRasterRenderPass<CleanupPassData>("Cleanup SSAO", out var passData, profilingSampler))
|
|
{
|
|
passData.directLightingStrength = directLightingStrength;
|
|
|
|
builder.AllowGlobalStateModification(true);
|
|
|
|
builder.UseTexture(finalTexture, AccessFlags.Read);
|
|
builder.SetGlobalTextureAfterPass(finalTexture, ShaderConstants._SSAOFinalTexture);
|
|
|
|
builder.SetRenderFunc(static (CleanupPassData data, RasterGraphContext ctx) =>
|
|
{
|
|
// We only want URP shaders to sample SSAO if After Opaque is disabled...
|
|
ctx.cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, true);
|
|
ctx.cmd.SetGlobalVector(ShaderConstants._AmbientOcclusionParam, new Vector4(1f, 0f, 0f, data.directLightingStrength));
|
|
});
|
|
}
|
|
}
|
|
|
|
// ---- Texture Creation ----
|
|
internal static TextureDesc GetCameraColorDescriptor(RenderGraph renderGraph, in TextureHandle source, bool useDynamicScale)
|
|
{
|
|
var info = renderGraph.GetRenderTargetInfo(source);
|
|
return new TextureDesc(info.width, info.height)
|
|
{
|
|
format = info.format,
|
|
slices = info.volumeDepth,
|
|
dimension = info.volumeDepth > 1 ? TextureDimension.Tex2DArray : TextureDimension.Tex2D,
|
|
bindTextureMS = info.bindMS,
|
|
useDynamicScale = useDynamicScale,
|
|
};
|
|
}
|
|
|
|
internal static void CreateRenderTextureHandles(
|
|
RenderGraph renderGraph,
|
|
UniversalResourceData resourceData,
|
|
in TextureDesc cameraColorDesc,
|
|
ScreenSpaceAmbientOcclusionSettings settings,
|
|
bool supportsR8,
|
|
BlurTypes blurType,
|
|
bool enableRandomWrite,
|
|
out TextureHandle aoTexture,
|
|
out TextureHandle blurTexture,
|
|
out TextureHandle temporalTexture,
|
|
out TextureHandle finalTexture)
|
|
{
|
|
// Descriptor for the final blur pass
|
|
TextureDesc finalTextureDescriptor = PostProcessUtils.GetCompatibleDescriptor(
|
|
cameraColorDesc,
|
|
cameraColorDesc.width,
|
|
cameraColorDesc.height,
|
|
supportsR8 ? GraphicsFormat.R8_UNorm : GraphicsFormat.R8G8B8A8_UNorm);
|
|
finalTextureDescriptor.enableRandomWrite = enableRandomWrite;
|
|
|
|
// Descriptor for the AO and Blur passes
|
|
int downsampleDivider = settings.Downsample ? 2 : 1;
|
|
bool useRedComponentOnly = supportsR8 && blurType > BlurTypes.Bilateral;
|
|
|
|
TextureDesc aoBlurDescriptor = PostProcessUtils.GetCompatibleDescriptor(
|
|
cameraColorDesc,
|
|
cameraColorDesc.width / downsampleDivider,
|
|
cameraColorDesc.height / downsampleDivider,
|
|
useRedComponentOnly ? GraphicsFormat.R8_UNorm : GraphicsFormat.R8G8B8A8_UNorm);
|
|
aoBlurDescriptor.enableRandomWrite = enableRandomWrite;
|
|
|
|
// Handles
|
|
aoTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_OcclusionTexture0", false, Color.clear, FilterMode.Bilinear);
|
|
finalTexture = settings.AfterOpaque ? resourceData.activeColorTexture : UniversalRenderer.CreateRenderGraphTexture(renderGraph, finalTextureDescriptor, k_SSAOTextureName, false, Color.clear, FilterMode.Bilinear);
|
|
|
|
if (settings.BlurQuality != ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low)
|
|
blurTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_OcclusionTexture1", false, Color.clear, FilterMode.Bilinear);
|
|
else
|
|
blurTexture = TextureHandle.nullHandle;
|
|
|
|
#if MODERN_SSAO
|
|
if (settings.IsTemporalFilterActive)
|
|
temporalTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_TemporalTexture", false, Color.clear, FilterMode.Bilinear);
|
|
else
|
|
temporalTexture = TextureHandle.nullHandle;
|
|
#else
|
|
temporalTexture = TextureHandle.nullHandle;
|
|
#endif
|
|
|
|
if (!settings.AfterOpaque)
|
|
resourceData.ssaoTexture = finalTexture;
|
|
}
|
|
}
|
|
}
|