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.
 
 
 
 

333 lines
14 KiB

using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
namespace FidelityFX
{
/// <summary>
/// Port of ffx_cacao.cpp/.h
/// </summary>
public static class Cacao
{
/// <summary>
/// Matrix to flip a couple of axes from Unity view space to CACAO's expected view space.
/// </summary>
public static Matrix4x4 UnityToCacaoViewMatrix = Matrix4x4.Scale(new Vector3(1f, -1f, -1f));
/// <summary>
/// The quality levels that FidelityFX CACAO can generate SSAO at. This affects the number of samples taken for generating SSAO.
/// </summary>
public enum Quality
{
Lowest = 0,
Low = 1,
Medium = 2,
High = 3,
Highest = 4,
}
/// <summary>
/// A structure for the settings used by FidelityFX CACAO. These settings may be updated with each draw call.
/// </summary>
[Serializable]
public struct Settings
{
[Range(0, 10), Tooltip("World (view) space size of the occlusion sphere.")]
public float radius;
[Range(0, 5), Tooltip("Effect strength linear multiplier.")]
public float shadowMultiplier;
[Range(0.5f, 5), Tooltip("Effect strength pow modifier.")]
public float shadowPower;
[Range(0, 1), Tooltip("Effect max limit (applied after multiplier but before blur).")]
public float shadowClamp;
[Range(0, 0.2f), Tooltip("Limits self-shadowing (makes the sampling area less of a hemisphere, more of a spherical cone, to avoid self-shadowing and various artifacts due to low tessellation and depth buffer imprecision, etc.).")]
public float horizonAngleThreshold;
[Range(0, 1000), Tooltip("Distance to start fading out the effect.")]
public float fadeOutFrom;
[Range(0, 1000), Tooltip("Distance at which the effect is faded out.")]
public float fadeOutTo;
[Tooltip("Effect quality, affects number of taps etc.")]
public Quality qualityLevel;
[Range(0, 1), Tooltip("Only for quality level Highest.")]
public float adaptiveQualityLimit;
[Range(0, 8), Tooltip("Number of edge-sensitive smart blur passes to apply.")]
public uint blurPassCount;
[Range(0, 1), Tooltip("How much to bleed over edges; 1: not at all, 0.5: half-half; 0.0: completely ignore edges.")]
public float sharpness;
[Range(0, Mathf.PI), Tooltip("Used to rotate sampling kernel; If using temporal AA / supersampling, suggested to rotate by ( (frame%3)/3.0*PI ) or similar. Kernel is already symmetrical, which is why we use PI and not 2*PI.")]
public float temporalSupersamplingAngleOffset;
[Range(0, 2), Tooltip("Used to scale sampling kernel; If using temporal AA / supersampling, suggested to scale by ( 1.0f + (((frame%3)-1.0)/3.0)*0.1 ) or similar.")]
public float temporalSupersamplingRadiusOffset;
[Range(0, 5), Tooltip("Used for high-res detail AO using neighboring depth pixels: adds a lot of detail but also reduces temporal stability (adds aliasing).")]
public float detailShadowStrength;
[Tooltip("This option should be set to true if FidelityFX-CACAO should reconstruct a normal buffer from the depth buffer. It is required to be true if no normal buffer is provided.")]
public bool generateNormals;
[Tooltip("Sigma squared value for use in bilateral upsampler giving Gaussian blur term. Should be greater than 0.0.")]
public float bilateralSigmaSquared;
[Tooltip("Sigma squared value for use in bilateral upsampler giving similarity weighting for neighbouring pixels. Should be greater than 0.0.")]
public float bilateralSimilarityDistanceSigma;
}
public static readonly Settings DefaultSettings = new()
{
radius = 1.2f,
shadowMultiplier = 1.0f,
shadowPower = 1.5f,
shadowClamp = 0.98f,
horizonAngleThreshold = 0.06f,
fadeOutFrom = 50f,
fadeOutTo = 300f,
qualityLevel = Quality.Highest,
adaptiveQualityLimit = 0.45f,
blurPassCount = 2,
sharpness = 0.98f,
temporalSupersamplingAngleOffset = 0f,
temporalSupersamplingRadiusOffset = 0f,
detailShadowStrength = 0.5f,
generateNormals = false,
bilateralSigmaSquared = 5f,
bilateralSimilarityDistanceSigma = 0.01f,
};
/// <summary>
/// The parameters necessary when changing the screen size of FidelityFX CACAO.
/// </summary>
public struct ScreenSizeInfo
{
public uint Width;
public uint Height;
public bool UseDownsampledSsao;
}
/// <summary>
/// A structure containing all of the input and output render targets used by FidelityFX CACAO.
/// </summary>
public struct DispatchInfo
{
public ResourceView DepthView;
public ResourceView? NormalsView; // This will be ignored if GenerateNormals is true
public ResourceView OutputView;
}
/// <summary>
/// A structure containing sizes of each of the buffers used by FidelityFX CACAO.
/// </summary>
internal struct BufferSizeInfo
{
public uint InputOutputBufferWidth;
public uint InputOutputBufferHeight;
public uint SsaoBufferWidth;
public uint SsaoBufferHeight;
public uint DepthBufferXOffset;
public uint DepthBufferYOffset;
public uint DepthBufferWidth;
public uint DepthBufferHeight;
public uint DeinterleavedDepthBufferXOffset;
public uint DeinterleavedDepthBufferYOffset;
public uint DeinterleavedDepthBufferWidth;
public uint DeinterleavedDepthBufferHeight;
public uint ImportanceMapWidth;
public uint ImportanceMapHeight;
public uint DownsampledSsaoBufferWidth;
public uint DownsampledSsaoBufferHeight;
}
/// <summary>
/// A C# structure for the constant buffer used by FidelityFX CACAO.
/// </summary>
[Serializable, StructLayout(LayoutKind.Sequential)]
internal struct Constants
{
public Vector2 DepthUnpackConsts;
public Vector2 CameraTanHalfFOV;
public Vector2 NDCToViewMul;
public Vector2 NDCToViewAdd;
public Vector2 DepthBufferUVToViewMul;
public Vector2 DepthBufferUVToViewAdd;
public float EffectRadius;
public float EffectShadowStrength;
public float EffectShadowPow;
public float EffectShadowClamp;
public float EffectFadeOutMul;
public float EffectFadeOutAdd;
public float EffectHorizonAngleThreshold;
public float EffectSamplingRadiusNearLimitRec;
public float DepthPrecisionOffsetMod;
public float NegRecEffectRadius;
public float LoadCounterAvgDiv;
public float AdaptiveSampleCountLimit;
public float InvSharpness;
public int BlurNumPasses;
public float BilateralSigmaSquared;
public float BilateralSimilarityDistanceSigma;
public PatternRotScaleMatrices PatternRotScaleMatrices0;
public PatternRotScaleMatrices PatternRotScaleMatrices1;
public PatternRotScaleMatrices PatternRotScaleMatrices2;
public PatternRotScaleMatrices PatternRotScaleMatrices3;
public float NormalsUnpackMul;
public float NormalsUnpackAdd;
public float DetailAOStrength;
public int Dummy0;
public Vector2 SSAOBufferDimensions;
public Vector2 SSAOBufferInverseDimensions;
public Vector2 DepthBufferDimensions;
public Vector2 DepthBufferInverseDimensions;
public Vector2Int DepthBufferOffset;
public Vector2Int Pad;
public Vector4 PerPassFullResUVOffset0;
public Vector4 PerPassFullResUVOffset1;
public Vector4 PerPassFullResUVOffset2;
public Vector4 PerPassFullResUVOffset3;
public Vector2 InputOutputBufferDimensions;
public Vector2 InputOutputBufferInverseDimensions;
public Vector2 ImportanceMapDimensions;
public Vector2 ImportanceMapInverseDimensions;
public Vector2 DeinterleavedDepthBufferDimensions;
public Vector2 DeinterleavedDepthBufferInverseDimensions;
public Vector2 DeinterleavedDepthBufferOffset;
public Vector2 DeinterleavedDepthBufferNormalisedOffset;
public Matrix4x4 NormalsWorldToViewspaceMatrix;
public Vector4 RTHandleScale;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
internal struct PatternRotScaleMatrices
{
// This is a really ugly solution, but fixed-size arrays are not blittable by Unity
public Vector4 PatternRotScaleMatrix0;
public Vector4 PatternRotScaleMatrix1;
public Vector4 PatternRotScaleMatrix2;
public Vector4 PatternRotScaleMatrix3;
public Vector4 PatternRotScaleMatrix4;
}
// Nasty workaround for the fact that we can't store these matrices in an array
internal static ref Vector4 GetPerPassFullResUVOffset(this ref Constants consts, int index)
{
switch (index)
{
case 0:
return ref consts.PerPassFullResUVOffset0;
case 1:
return ref consts.PerPassFullResUVOffset1;
case 2:
return ref consts.PerPassFullResUVOffset2;
case 3:
return ref consts.PerPassFullResUVOffset3;
default:
throw new IndexOutOfRangeException();
}
}
// Nasty workaround for the fact that we can't store these matrices in an array
internal static ref PatternRotScaleMatrices GetPatternRotScaleMatrices(this ref Constants consts, int index)
{
switch (index)
{
case 0:
return ref consts.PatternRotScaleMatrices0;
case 1:
return ref consts.PatternRotScaleMatrices1;
case 2:
return ref consts.PatternRotScaleMatrices2;
case 3:
return ref consts.PatternRotScaleMatrices3;
default:
throw new IndexOutOfRangeException();
}
}
// Nasty workaround for the fact that we can't store these matrices in an array
internal static ref Vector4 GetPatternRotScaleMatrix(this ref PatternRotScaleMatrices matrices, int index)
{
switch (index)
{
case 0:
return ref matrices.PatternRotScaleMatrix0;
case 1:
return ref matrices.PatternRotScaleMatrix1;
case 2:
return ref matrices.PatternRotScaleMatrix2;
case 3:
return ref matrices.PatternRotScaleMatrix3;
case 4:
return ref matrices.PatternRotScaleMatrix4;
default:
throw new IndexOutOfRangeException();
}
}
/// <summary>
/// An immutable structure wrapping all of the necessary information to bind a specific buffer or attachment of a render target to a compute shader.
/// </summary>
public readonly struct ResourceView
{
/// <summary>
/// This value is the equivalent of not setting any value at all; all struct fields will have their default values.
/// It does not refer to a valid texture, therefore any variable set to this value should be checked for IsValid and reassigned before being bound to a shader.
/// </summary>
public static readonly ResourceView Unassigned = new ResourceView(default);
/// <summary>
/// This value contains a valid texture reference that can be bound to a shader, however it is just an empty placeholder texture.
/// Binding this to a shader can be seen as setting the texture variable inside the shader to null.
/// </summary>
public static readonly ResourceView None = new ResourceView(BuiltinRenderTextureType.None);
public ResourceView(in RenderTargetIdentifier renderTarget, RenderTextureSubElement subElement = RenderTextureSubElement.Default, int mipLevel = 0)
{
RenderTarget = renderTarget;
SubElement = subElement;
MipLevel = mipLevel;
}
public bool IsValid => !RenderTarget.Equals(default);
public readonly RenderTargetIdentifier RenderTarget;
public readonly RenderTextureSubElement SubElement;
public readonly int MipLevel;
}
}
}