// Copyright (c) <2015> // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE.TXT) // AUTHOR: Lasse Jon Fuglsang Pedersen #if UNITY_5_5_OR_NEWER #define SUPPORT_STEREO #endif using UnityEngine; [ExecuteInEditMode] [RequireComponent(typeof(Camera), typeof(FrustumJitter), typeof(VelocityBuffer))] [AddComponentMenu("Playdead/TemporalReprojection")] public class TemporalReprojection : EffectBase { private static RenderBuffer[] mrt = new RenderBuffer[2]; private Camera _camera; private FrustumJitter _frustumJitter; private VelocityBuffer _velocityBuffer; #region dithering public Texture ditherTex = null; private const int NUM_DITHEROFS = 1024; const float DITHERSIZ = 256; private static int frameofs = 0; private static Vector4[] ditheroffsets = null; public static Vector4 GetFrame_DitherOffset() { if ( ditheroffsets == null ) init_ditheroffsets(); frameofs = (frameofs+1) % NUM_DITHEROFS; return ditheroffsets[frameofs]; } private static void init_ditheroffsets() { if ( ditheroffsets != null ) return; ditheroffsets = new Vector4[NUM_DITHEROFS]; for ( int i=0, n=ditheroffsets.Length; i(); _frustumJitter = GetComponent(); _velocityBuffer = GetComponent(); } void Clear() { EnsureArray(ref reprojectionIndex, 2); reprojectionIndex[0] = -1; reprojectionIndex[1] = -1; } void Awake() { Reset(); Clear(); } void Resolve(RenderTexture source, RenderTexture destination) { EnsureArray(ref reprojectionBuffer, 2, 2); EnsureArray(ref reprojectionIndex, 2, initialValue: -1); EnsureMaterial(ref reprojectionMaterial, reprojectionShader); if (reprojectionMaterial == null) { Graphics.Blit(source, destination); return; } #if SUPPORT_STEREO int eyeIndex = (_camera.stereoActiveEye == Camera.MonoOrStereoscopicEye.Right) ? 1 : 0; #else int eyeIndex = 0; #endif int bufferW = source.width; int bufferH = source.height; if (EnsureRenderTarget(ref reprojectionBuffer[eyeIndex, 0], bufferW, bufferH, RenderTextureFormat.ARGB32, FilterMode.Bilinear, antiAliasing: source.antiAliasing)) Clear(); if (EnsureRenderTarget(ref reprojectionBuffer[eyeIndex, 1], bufferW, bufferH, RenderTextureFormat.ARGB32, FilterMode.Bilinear, antiAliasing: source.antiAliasing)) Clear(); #if SUPPORT_STEREO bool stereoEnabled = _camera.stereoEnabled; #else bool stereoEnabled = false; #endif #if UNITY_EDITOR bool allowMotionBlur = !stereoEnabled && Application.isPlaying; #else bool allowMotionBlur = !stereoEnabled; #endif EnsureKeyword(reprojectionMaterial, "CAMERA_PERSPECTIVE", !_camera.orthographic); EnsureKeyword(reprojectionMaterial, "CAMERA_ORTHOGRAPHIC", _camera.orthographic); //EnsureKeyword(reprojectionMaterial, "UNJITTER_COLORSAMPLES", unjitterColorSamples); //EnsureKeyword(reprojectionMaterial, "UNJITTER_NEIGHBORHOOD", unjitterNeighborhood); //EnsureKeyword(reprojectionMaterial, "UNJITTER_REPROJECTION", unjitterReprojection); EnsureKeyword(reprojectionMaterial, "USE_CHROMA_COLORSPACE", colorspace == ColorSpace.Chroma); EnsureKeyword(reprojectionMaterial, "USE_CLIPPING", historyRectification == HistoryRectification.CenterClipping || historyRectification == HistoryRectification.Clipping ); EnsureKeyword(reprojectionMaterial, "USE_CENTER_CLIPPING", historyRectification == HistoryRectification.CenterClipping ); EnsureKeyword(reprojectionMaterial, "USE_VARIANCE_CLIPPING", useVarianceClipping ); EnsureKeyword(reprojectionMaterial, "USE_AABB_ROUNDING", useAABBRounding ); EnsureKeyword(reprojectionMaterial, "USE_DILATION_5X", dilation == Dilation.Dilate5X); EnsureKeyword(reprojectionMaterial, "USE_DILATION_3X3", dilation == Dilation.Dilate3X3); EnsureKeyword(reprojectionMaterial, "USE_MOTION_BLUR", useMotionBlur && allowMotionBlur); EnsureKeyword(reprojectionMaterial, "USE_MOTION_BLUR_NEIGHBORMAX", _velocityBuffer.activeVelocityNeighborMax != null); EnsureKeyword(reprojectionMaterial, "USE_HIGHER_ORDER_TEXTURE_FILTERING_COLOR", useHigherOrderFilteringMainColor); EnsureKeyword(reprojectionMaterial, "USE_HIGHER_ORDER_TEXTURE_FILTERING_HISTORY", useHigherOrderFilteringHistory); if (reprojectionIndex[eyeIndex] == -1)// bootstrap { reprojectionIndex[eyeIndex] = 0; reprojectionBuffer[eyeIndex, reprojectionIndex[eyeIndex]].DiscardContents(); Graphics.Blit(source, reprojectionBuffer[eyeIndex, reprojectionIndex[eyeIndex]]); } int indexRead = reprojectionIndex[eyeIndex]; int indexWrite = (reprojectionIndex[eyeIndex] + 1) % 2; Vector4 jitterUV = _frustumJitter.activeSample; jitterUV.x /= source.width; jitterUV.y /= source.height; jitterUV.z /= source.width; jitterUV.w /= source.height; reprojectionMaterial.SetVector(JitterUVID(), jitterUV); reprojectionMaterial.SetTexture(VelocityBufferID(), _velocityBuffer.activeVelocityBuffer); reprojectionMaterial.SetTexture(VelocityNeighborMaxID(), _velocityBuffer.activeVelocityNeighborMax); reprojectionMaterial.SetTexture(MainTexID(), source); reprojectionMaterial.SetTexture(PrevTexID(), reprojectionBuffer[eyeIndex, indexRead]); reprojectionMaterial.SetFloat(FeedbackMinID(), feedbackMin); reprojectionMaterial.SetFloat(FeedbackMinMaxID(), feedbackMax - feedbackMin); reprojectionMaterial.SetFloat(MotionScaleID(), motionBlurStrength * (motionBlurIgnoreFF ? Mathf.Min(1.0f, 1.0f / _velocityBuffer.timeScale) : 1.0f)); reprojectionMaterial.SetTexture(DitherTexID(), ditherTex); Vector4 ofs = GetFrame_DitherOffset(); reprojectionMaterial.SetVector( DitherOffsetLocalID(), GetFrame_DitherOffset() ); // reproject frame n-1 into output + history buffer { mrt[0] = reprojectionBuffer[eyeIndex, indexWrite].colorBuffer; mrt[1] = destination.colorBuffer; Graphics.SetRenderTarget(mrt, source.depthBuffer); reprojectionMaterial.SetPass(0); reprojectionBuffer[eyeIndex, indexWrite].DiscardContents(); //DrawFullscreenQuad(); DrawFullscreenTri(); reprojectionIndex[eyeIndex] = indexWrite; } } void OnRenderImage(RenderTexture source, RenderTexture destination) { if (destination != null && source.antiAliasing == destination.antiAliasing)// resolve without additional blit when not end of chain { Resolve(source, destination); } else { RenderTexture internalDestination = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, source.antiAliasing); { Resolve(source, internalDestination); Graphics.Blit(internalDestination, destination); } RenderTexture.ReleaseTemporary(internalDestination); } } void OnApplicationQuit() { if (reprojectionBuffer != null) { ReleaseRenderTarget(ref reprojectionBuffer[0, 0]); ReleaseRenderTarget(ref reprojectionBuffer[0, 1]); ReleaseRenderTarget(ref reprojectionBuffer[1, 0]); ReleaseRenderTarget(ref reprojectionBuffer[1, 1]); } } }