using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace GameOldBoy.Rendering { public enum RenderingMode { Forward, Deferred } public class TAA : ScriptableRendererFeature { public bool PreviewInSceneView = true; public bool UseMotionVector; public RenderingMode RenderingMode = RenderingMode.Forward; public bool WorkOnPrepass = false; public bool IgonreTransparentObject = false; public bool Use32Bit = false; [HideInInspector] public Shader Shader; Material material; TAACameraPass m_TAACameraPrepass; TAACameraPass m_TAACameraPass; TAAPass m_TAAPass; Dictionary haltonSequences = new Dictionary(); public override void Create() { #if UNITY_EDITOR foreach (var guid in AssetDatabase.FindAssets("TAA t:Shader")) { var path = AssetDatabase.GUIDToAssetPath(guid); if (path.Contains("URP TAA/Shaders/TAA.shader")) { Shader = AssetDatabase.LoadAssetAtPath(path); break; } } #endif m_TAACameraPrepass = new TAACameraPass(name); #if UNITY_2021_1_OR_NEWER m_TAACameraPrepass.renderPassEvent = RenderPassEvent.BeforeRenderingPrePasses; #else m_TAACameraPrepass.renderPassEvent = RenderPassEvent.BeforeRenderingPrepasses; #endif m_TAACameraPass = new TAACameraPass(name); #if UNITY_2021_2_OR_NEWER switch (RenderingMode) { case RenderingMode.Forward: m_TAACameraPass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques; //m_TAACameraPass.renderPassEvent = RenderPassEvent.AfterRendering; break; case RenderingMode.Deferred: m_TAACameraPass.renderPassEvent = RenderPassEvent.BeforeRenderingGbuffer; break; } #else m_TAACameraPass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques; #endif m_TAAPass = new TAAPass(name); if (IgonreTransparentObject) { m_TAAPass.renderPassEvent = RenderPassEvent.BeforeRenderingTransparents; } else { m_TAAPass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; } } bool getMaterial() { if (material == null) { material = CoreUtils.CreateEngineMaterial(Shader); if (material == null) { return false; } else { return true; } } else { return true; } } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { var camera = renderingData.cameraData.camera; var isSceneViewCamera = renderingData.cameraData.isSceneViewCamera; TAAComponent taa; if (isSceneViewCamera && Camera.main != null) { taa = Camera.main.GetComponent(); } else { taa = camera.GetComponent(); } if (taa != null) { if (getMaterial() && (isSceneViewCamera ? PreviewInSceneView : true) && taa.Enabled) { var hash = camera.GetHashCode(); var proj = camera.projectionMatrix; var view = camera.worldToCameraMatrix; var viewProj = proj * view; HaltonSequence haltonSequence; if (haltonSequences.ContainsKey(hash)) { haltonSequence = haltonSequences[hash]; } else { haltonSequence = new HaltonSequence(1024); } if (haltonSequence.prevViewProj == Matrix4x4.zero) { haltonSequence.prevViewProj = viewProj; } haltonSequence.Get(out var offsetX, out var offsetY); var matrix = camera.projectionMatrix; var descriptor = renderingData.cameraData.cameraTargetDescriptor; if (camera.orthographic) { matrix[0, 3] -= (offsetX * 2 - 1) / descriptor.width; matrix[1, 3] -= (offsetY * 2 - 1) / descriptor.height; } else { matrix[0, 2] += (offsetX * 2 - 1) / descriptor.width; matrix[1, 2] += (offsetY * 2 - 1) / descriptor.height; } var offset = new Vector2( (offsetX - 0.5f) / descriptor.width, (offsetY - 0.5f) / descriptor.height); if (WorkOnPrepass) { m_TAACameraPrepass.Setup(matrix); renderer.EnqueuePass(m_TAACameraPrepass); } m_TAACameraPass.Setup(matrix); renderer.EnqueuePass(m_TAACameraPass); m_TAAPass.Setup( renderer, Use32Bit, material, haltonSequence.prevViewProj, offset, taa, UseMotionVector, RenderingMode); renderer.EnqueuePass(m_TAAPass); haltonSequence.prevViewProj = viewProj; haltonSequence.frameCount = Time.frameCount; haltonSequences[hash] = haltonSequence; } } if (haltonSequences.Count > 0) { //Span rmArr = stackalloc int[8]; int[] rmArr = new int[8]; int index = 0; foreach (var item in haltonSequences) { var haltonSequence = item.Value; if (Time.frameCount - haltonSequence.frameCount > 10) { rmArr[index] = item.Key; if (++index == 8) break; } } if (index > 0) { for (int i = 0; i < index; i++) { haltonSequences.Remove(rmArr[i]); m_TAAPass.CleanUp(rmArr[i]); } if (haltonSequences.Count == 0) { CoreUtils.Destroy(material); material = null; } } } } //void cleanup() //{ // int[] rmArr = new int[haltonSequences.Count]; // int index = 0; // foreach (var item in haltonSequences) // { // var haltonSequence = item.Value; // rmArr[index++] = item.Key; // } // if (index > 0) // { // for (int i = 0; i < rmArr.Length; i++) // { // haltonSequences.Remove(rmArr[i]); // m_TAAPass.CleanUp(rmArr[i]); // } // } // CoreUtils.Destroy(material); // material = null; //} //void OnDisable() //{ // cleanup(); //} //protected override void Dispose(bool disposing) //{ // cleanup(); // base.Dispose(disposing); //} class TAACameraPass : ScriptableRenderPass { #if UNITY_2019_4_OR_NEWER ProfilingSampler m_ProfilingSampler; #else string m_ProfilingSampler; #endif Matrix4x4 matrix; public TAACameraPass(string profilerTag) { #if UNITY_2019_4_OR_NEWER m_ProfilingSampler = new ProfilingSampler(profilerTag); #else m_ProfilingSampler = profilerTag; #endif } public void Setup(Matrix4x4 matrix) { this.matrix = matrix; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { #if UNITY_2020_1_OR_NEWER var cmd = CommandBufferPool.Get(); #else #if UNITY_2019_4_OR_NEWER var cmd = CommandBufferPool.Get(m_ProfilingSampler.name); #else var cmd = CommandBufferPool.Get(m_ProfilingSampler); #endif #endif #if UNITY_2019_4_OR_NEWER using (new ProfilingScope(cmd, m_ProfilingSampler)) #else using (new ProfilingSample(cmd, m_ProfilingSampler)) #endif { var camera = renderingData.cameraData.camera; cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, matrix); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } class TAAPass : ScriptableRenderPass { #if UNITY_2019_4_OR_NEWER ProfilingSampler m_ProfilingSampler; #else string m_ProfilingSampler; #endif ScriptableRenderer renderer; bool use32Bit; Material material; Matrix4x4 prevViewProj; Vector2 offset; float blend; bool antiGhosting; bool useSharpenFilter; bool useBicubicFilter; bool useClipAABB; bool useDilation; bool useTonemap; bool useVarianceClipping; bool useYCoCgSpace; float gamma; float sharp; float prevSharp; bool useMotionVector; bool use4Tap; class TAATextureSwap { public RenderTexture TAATextureA { get; } public RenderTexture TAATextureB { get; } public RenderTextureDescriptor Descriptor { get; } bool swap; public TAATextureSwap(RenderTexture taaTextureA, RenderTexture taaTextureB, RenderTextureDescriptor descriptor) { TAATextureA = taaTextureA; TAATextureB = taaTextureB; Descriptor = descriptor; swap = true; } public bool Swap() { swap = !swap; return swap; } public bool CheckTextureNull() { return TAATextureA == null || TAATextureB == null; } public void Release() { if (TAATextureA != null) { TAATextureA.Release(); } if(TAATextureB != null) { TAATextureB.Release(); } } } Dictionary taaTextures = new Dictionary(); int taaPrevViewProj = Shader.PropertyToID("_TAA_PrevViewProj"); int taaOffset = Shader.PropertyToID("_TAA_Offset"); int taaParams0 = Shader.PropertyToID("_TAA_Params0"); int taaSourceTex = Shader.PropertyToID("_SourceTex"); int taaTexture = Shader.PropertyToID("_TAA_Texture"); public TAAPass(string profilerTag) { #if UNITY_2019_4_OR_NEWER m_ProfilingSampler = new ProfilingSampler(profilerTag); #else m_ProfilingSampler = profilerTag; #endif } public void Setup( ScriptableRenderer renderer, bool use32Bit, Material material, Matrix4x4 prevViewProj, Vector2 offset, TAAComponent taa, bool useMotionVector, RenderingMode renderingMode) { this.renderer = renderer; this.use32Bit = use32Bit; this.material = material; this.prevViewProj = prevViewProj; this.offset = offset; blend = taa.Blend; antiGhosting = taa.AntiGhosting; useSharpenFilter = taa.UseBlurSharpenFilter; useBicubicFilter = taa.UseBicubicFilter; useClipAABB = taa.UseClipAABB; useDilation = taa.UseDilation; useTonemap = taa.UseTonemap; useVarianceClipping = taa.UseVarianceClipping; useYCoCgSpace = taa.UseYCoCgSpace; gamma = taa.Stability; sharp = taa.SharpenStrength; prevSharp = taa.HistorySharpening; this.useMotionVector = useMotionVector; use4Tap = taa.Use4Tap; #if UNITY_2021_2_OR_NEWER switch (renderingMode) { case RenderingMode.Forward: if (useMotionVector) { ConfigureInput(ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Motion); } else { ConfigureInput(ScriptableRenderPassInput.Depth); } break; case RenderingMode.Deferred: if (useMotionVector) { ConfigureInput(ScriptableRenderPassInput.Motion); } break; } #else #if UNITY_2020_1_OR_NEWER ConfigureInput(ScriptableRenderPassInput.Depth); #endif #endif } void allocRT(out RenderTexture rt_a, out RenderTexture rt_b, RenderTextureDescriptor descriptor) { rt_a = new RenderTexture(descriptor); rt_a.filterMode = FilterMode.Bilinear; rt_b = new RenderTexture(descriptor); rt_b.filterMode = FilterMode.Bilinear; } void allocRT(out RenderTexture rt_a, out RenderTexture rt_b, CommandBuffer cmd, ScriptableRenderContext context, RenderTargetIdentifier rtid, RenderTextureDescriptor descriptor) { allocRT(out rt_a, out rt_b, descriptor); cmd.SetGlobalTexture(taaSourceTex, rtid); cmd.SetRenderTarget(rt_a); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 1); cmd.SetRenderTarget(rt_b); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 1); context.ExecuteCommandBuffer(cmd); cmd.Clear(); } void genRT(CommandBuffer cmd, ScriptableRenderContext context, RenderTargetIdentifier rtid, RenderTextureDescriptor descriptor, int hash) { descriptor.useMipMap = false; descriptor.autoGenerateMips = false; descriptor.depthBufferBits = 0; descriptor.msaaSamples = 1; if (use32Bit) descriptor.colorFormat = RenderTextureFormat.ARGBFloat; if (taaTextures.ContainsKey(hash)) { if (taaTextures[hash].CheckTextureNull()) { allocRT(out var taaTextureA, out var taaTextureB, cmd, context, rtid, descriptor); taaTextures[hash] = new TAATextureSwap(taaTextureA, taaTextureB, descriptor); return; } } else { allocRT(out var taaTextureA, out var taaTextureB, cmd, context, rtid, descriptor); taaTextures[hash] = new TAATextureSwap(taaTextureA, taaTextureB, descriptor); return; } var desc = taaTextures[hash].Descriptor; if (desc.width != descriptor.width || desc.height != descriptor.height || desc.colorFormat != descriptor.colorFormat) { taaTextures[hash].Release(); allocRT(out var taaTextureA, out var taaTextureB, cmd, context, rtid, descriptor); taaTextures[hash] = new TAATextureSwap(taaTextureA, taaTextureB, descriptor); } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { #if UNITY_2020_1_OR_NEWER var cmd = CommandBufferPool.Get(); #else #if UNITY_2019_4_OR_NEWER var cmd = CommandBufferPool.Get(m_ProfilingSampler.name); #else var cmd = CommandBufferPool.Get(m_ProfilingSampler); #endif #endif #if UNITY_2019_4_OR_NEWER using (new ProfilingScope(cmd, m_ProfilingSampler)) #else using (new ProfilingSample(cmd, m_ProfilingSampler)) #endif { var camera = renderingData.cameraData.camera; var cameraColorTarget = renderer.cameraColorTarget; var descriptor = renderingData.cameraData.cameraTargetDescriptor; var hash = renderingData.cameraData.camera.GetHashCode(); cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity); genRT(cmd, context, cameraColorTarget, descriptor, hash); material.SetMatrix(taaPrevViewProj, prevViewProj); cmd.SetGlobalVector(taaOffset, offset); material.SetVector(taaParams0, new Vector4(blend, gamma, sharp, prevSharp)); CoreUtils.SetKeyword(material, "_TAA_AntiGhosting", antiGhosting); #if UNITY_2021_2_OR_NEWER CoreUtils.SetKeyword(material, "_TAA_UseMotionVector", renderingData.cameraData.isSceneViewCamera ? false : useMotionVector); #else CoreUtils.SetKeyword(material, "_TAA_UseMotionVector", false); #endif CoreUtils.SetKeyword(material, "_TAA_UseBlurSharpenFilter", useSharpenFilter); CoreUtils.SetKeyword(material, "_TAA_UseBicubicFilter", useBicubicFilter); CoreUtils.SetKeyword(material, "_TAA_UseClipAABB", useClipAABB); CoreUtils.SetKeyword(material, "_TAA_UseDilation", useDilation); CoreUtils.SetKeyword(material, "_TAA_UseTonemap", useTonemap); CoreUtils.SetKeyword(material, "_TAA_UseVarianceClipping", useVarianceClipping); CoreUtils.SetKeyword(material, "_TAA_UseYCoCgSpace", useYCoCgSpace); CoreUtils.SetKeyword(material, "_TAA_Use4Tap", use4Tap); var taaTextureSwap = taaTextures[hash]; if (taaTextureSwap.Swap()) { cmd.SetGlobalTexture(taaSourceTex, cameraColorTarget); material.SetTexture(taaTexture, taaTextureSwap.TAATextureB); cmd.SetRenderTarget(taaTextureSwap.TAATextureA); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 0); cmd.SetGlobalTexture(taaSourceTex, taaTextureSwap.TAATextureA); cmd.SetRenderTarget(cameraColorTarget); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 1); } else { cmd.SetGlobalTexture(taaSourceTex, cameraColorTarget); material.SetTexture(taaTexture, taaTextureSwap.TAATextureA); cmd.SetRenderTarget(taaTextureSwap.TAATextureB); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 0); cmd.SetGlobalTexture(taaSourceTex, taaTextureSwap.TAATextureB); cmd.SetRenderTarget(cameraColorTarget); cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, material, 0, 1); } cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public void CleanUp(int hash) { if (taaTextures.ContainsKey(hash)) { taaTextures[hash].Release(); taaTextures.Remove(hash); } } } struct HaltonSequence { int count; int index; float[] arrX; float[] arrY; public Matrix4x4 prevViewProj; public int frameCount; public HaltonSequence(int count) { this.count = count; index = 0; arrX = new float[count]; arrY = new float[count]; prevViewProj = Matrix4x4.zero; frameCount = 0; for (int i = 0; i < arrX.Length; i++) { arrX[i] = get(i, 2); } for (int i = 0; i < arrY.Length; i++) { arrY[i] = get(i, 3); } } float get(int index, int @base) { float fraction = 1; float result = 0; while (index > 0) { fraction /= @base; result += fraction * (index % @base); index /= @base; } return result; } public void Get(out float x, out float y) { if (++index == count) index = 1; x = arrX[index]; y = arrY[index]; } } } }