Firstborn/Library/PackageCache/com.unity.postprocessing@3.2.2/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs

290 lines
12 KiB
C#
Raw Normal View History

2023-03-28 13:24:16 -04:00
using System;
namespace UnityEngine.Rendering.PostProcessing
{
/// <summary>
/// This class holds settings for the Temporal Anti-aliasing (TAA) effect.
/// </summary>
[UnityEngine.Scripting.Preserve]
[Serializable]
public sealed class TemporalAntialiasing
{
/// <summary>
/// The diameter (in texels) inside which jitter samples are spread. Smaller values result
/// in crisper but more aliased output, while larger values result in more stable but
/// blurrier output.
/// </summary>
[Tooltip("The diameter (in texels) inside which jitter samples are spread. Smaller values result in crisper but more aliased output, while larger values result in more stable, but blurrier, output.")]
[Range(0.1f, 1f)]
public float jitterSpread = 0.75f;
/// <summary>
/// Controls the amount of sharpening applied to the color buffer. High values may introduce
/// dark-border artifacts.
/// </summary>
[Tooltip("Controls the amount of sharpening applied to the color buffer. High values may introduce dark-border artifacts.")]
[Range(0f, 3f)]
public float sharpness = 0.25f;
/// <summary>
/// The blend coefficient for a stationary fragment. Controls the percentage of history
/// sample blended into the final color.
/// </summary>
[Tooltip("The blend coefficient for a stationary fragment. Controls the percentage of history sample blended into the final color.")]
[Range(0f, 0.99f)]
public float stationaryBlending = 0.95f;
/// <summary>
/// The blend coefficient for a fragment with significant motion. Controls the percentage of
/// history sample blended into the final color.
/// </summary>
[Tooltip("The blend coefficient for a fragment with significant motion. Controls the percentage of history sample blended into the final color.")]
[Range(0f, 0.99f)]
public float motionBlending = 0.85f;
/// <summary>
/// Sets a custom function that will be called to generate the jittered projection matrice.
/// </summary>
public Func<Camera, Vector2, Matrix4x4> jitteredMatrixFunc;
/// <summary>
/// The current jitter amount
/// </summary>
public Vector2 jitter { get; private set; }
enum Pass
{
SolverDilate,
SolverNoDilate
}
readonly RenderTargetIdentifier[] m_Mrt = new RenderTargetIdentifier[2];
bool m_ResetHistory = true;
const int k_SampleCount = 8;
/// <summary>
/// The current sample index.
/// </summary>
public int sampleIndex { get; private set; }
// Ping-pong between two history textures as we can't read & write the same target in the
// same pass
const int k_NumEyes = 2;
const int k_NumHistoryTextures = 2;
readonly RenderTexture[][] m_HistoryTextures = new RenderTexture[k_NumEyes][];
readonly int[] m_HistoryPingPong = new int[k_NumEyes];
/// <summary>
/// Returns <c>true</c> if the effect is currently enabled and supported.
/// </summary>
/// <returns><c>true</c> if the effect is currently enabled and supported</returns>
public bool IsSupported()
{
return SystemInfo.supportedRenderTargetCount >= 2
&& SystemInfo.supportsMotionVectors
#if !UNITY_2017_3_OR_NEWER
&& !RuntimeUtilities.isVREnabled
#endif
&& SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;
}
internal DepthTextureMode GetCameraFlags()
{
return DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
}
internal void ResetHistory()
{
m_ResetHistory = true;
}
Vector2 GenerateRandomOffset()
{
// The variance between 0 and the actual halton sequence values reveals noticeable instability
// in Unity's shadow maps, so we avoid index 0.
var offset = new Vector2(
HaltonSeq.Get((sampleIndex & 1023) + 1, 2) - 0.5f,
HaltonSeq.Get((sampleIndex & 1023) + 1, 3) - 0.5f
);
if (++sampleIndex >= k_SampleCount)
sampleIndex = 0;
return offset;
}
/// <summary>
/// Generates a jittered projection matrix for a given camera.
/// </summary>
/// <param name="camera">The camera to get a jittered projection matrix for.</param>
/// <returns>A jittered projection matrix.</returns>
public Matrix4x4 GetJitteredProjectionMatrix(Camera camera)
{
Matrix4x4 cameraProj;
jitter = GenerateRandomOffset();
jitter *= jitterSpread;
if (jitteredMatrixFunc != null)
{
cameraProj = jitteredMatrixFunc(camera, jitter);
}
else
{
cameraProj = camera.orthographic
? RuntimeUtilities.GetJitteredOrthographicProjectionMatrix(camera, jitter)
: RuntimeUtilities.GetJitteredPerspectiveProjectionMatrix(camera, jitter);
}
jitter = new Vector2(jitter.x / camera.pixelWidth, jitter.y / camera.pixelHeight);
return cameraProj;
}
/// <summary>
/// Prepares the jittered and non jittered projection matrices.
/// </summary>
/// <param name="context">The current post-processing context.</param>
public void ConfigureJitteredProjectionMatrix(PostProcessRenderContext context)
{
var camera = context.camera;
camera.nonJitteredProjectionMatrix = camera.projectionMatrix;
camera.projectionMatrix = GetJitteredProjectionMatrix(camera);
camera.useJitteredProjectionMatrixForTransparentRendering = false;
}
/// <summary>
/// Prepares the jittered and non jittered projection matrices for stereo rendering.
/// </summary>
/// <param name="context">The current post-processing context.</param>
// TODO: We'll probably need to isolate most of this for SRPs
public void ConfigureStereoJitteredProjectionMatrices(PostProcessRenderContext context)
{
#if UNITY_2017_3_OR_NEWER
var camera = context.camera;
jitter = GenerateRandomOffset();
jitter *= jitterSpread;
for (var eye = Camera.StereoscopicEye.Left; eye <= Camera.StereoscopicEye.Right; eye++)
{
// This saves off the device generated projection matrices as non-jittered
context.camera.CopyStereoDeviceProjectionMatrixToNonJittered(eye);
var originalProj = context.camera.GetStereoNonJitteredProjectionMatrix(eye);
// Currently no support for custom jitter func, as VR devices would need to provide
// original projection matrix as input along with jitter
var jitteredMatrix = RuntimeUtilities.GenerateJitteredProjectionMatrixFromOriginal(context, originalProj, jitter);
context.camera.SetStereoProjectionMatrix(eye, jitteredMatrix);
}
// jitter has to be scaled for the actual eye texture size, not just the intermediate texture size
// which could be double-wide in certain stereo rendering scenarios
jitter = new Vector2(jitter.x / context.screenWidth, jitter.y / context.screenHeight);
camera.useJitteredProjectionMatrixForTransparentRendering = false;
#endif
}
void GenerateHistoryName(RenderTexture rt, int id, PostProcessRenderContext context)
{
rt.name = "Temporal Anti-aliasing History id #" + id;
if (context.stereoActive)
rt.name += " for eye " + context.xrActiveEye;
}
RenderTexture CheckHistory(int id, PostProcessRenderContext context)
{
int activeEye = context.xrActiveEye;
if (m_HistoryTextures[activeEye] == null)
m_HistoryTextures[activeEye] = new RenderTexture[k_NumHistoryTextures];
var rt = m_HistoryTextures[activeEye][id];
if (m_ResetHistory || rt == null || !rt.IsCreated())
{
RenderTexture.ReleaseTemporary(rt);
rt = context.GetScreenSpaceTemporaryRT(0, context.sourceFormat);
GenerateHistoryName(rt, id, context);
rt.filterMode = FilterMode.Bilinear;
m_HistoryTextures[activeEye][id] = rt;
context.command.BlitFullscreenTriangle(context.source, rt);
}
else if (rt.width != context.width || rt.height != context.height)
{
// On size change, simply copy the old history to the new one. This looks better
// than completely discarding the history and seeing a few aliased frames.
var rt2 = context.GetScreenSpaceTemporaryRT(0, context.sourceFormat);
GenerateHistoryName(rt2, id, context);
rt2.filterMode = FilterMode.Bilinear;
m_HistoryTextures[activeEye][id] = rt2;
context.command.BlitFullscreenTriangle(rt, rt2);
RenderTexture.ReleaseTemporary(rt);
}
return m_HistoryTextures[activeEye][id];
}
internal void Render(PostProcessRenderContext context)
{
var sheet = context.propertySheets.Get(context.resources.shaders.temporalAntialiasing);
var cmd = context.command;
cmd.BeginSample("TemporalAntialiasing");
int pp = m_HistoryPingPong[context.xrActiveEye];
var historyRead = CheckHistory(++pp % 2, context);
var historyWrite = CheckHistory(++pp % 2, context);
m_HistoryPingPong[context.xrActiveEye] = ++pp % 2;
const float kMotionAmplification = 100f * 60f;
sheet.properties.SetVector(ShaderIDs.Jitter, jitter);
sheet.properties.SetFloat(ShaderIDs.Sharpness, sharpness);
sheet.properties.SetVector(ShaderIDs.FinalBlendParameters, new Vector4(stationaryBlending, motionBlending, kMotionAmplification, 0f));
sheet.properties.SetTexture(ShaderIDs.HistoryTex, historyRead);
// TODO: Account for different possible RenderViewportScale value from previous frame...
int pass = context.camera.orthographic ? (int)Pass.SolverNoDilate : (int)Pass.SolverDilate;
m_Mrt[0] = context.destination;
m_Mrt[1] = historyWrite;
cmd.BlitFullscreenTriangle(context.source, m_Mrt, context.source, sheet, pass);
cmd.EndSample("TemporalAntialiasing");
m_ResetHistory = false;
}
internal void Release()
{
if (m_HistoryTextures != null)
{
for (int i = 0; i < m_HistoryTextures.Length; i++)
{
if (m_HistoryTextures[i] == null)
continue;
for (int j = 0; j < m_HistoryTextures[i].Length; j++)
{
RenderTexture.ReleaseTemporary(m_HistoryTextures[i][j]);
m_HistoryTextures[i][j] = null;
}
m_HistoryTextures[i] = null;
}
}
sampleIndex = 0;
m_HistoryPingPong[0] = 0;
m_HistoryPingPong[1] = 0;
ResetHistory();
}
}
}