using UnityEngine.Rendering; 
 | 
  
 | 
namespace UnityEngine.PostProcessing 
 | 
{ 
 | 
    using Settings = MotionBlurModel.Settings; 
 | 
  
 | 
    public sealed class MotionBlurComponent : PostProcessingComponentCommandBuffer<MotionBlurModel> 
 | 
    { 
 | 
        static class Uniforms 
 | 
        { 
 | 
            internal static readonly int _VelocityScale     = Shader.PropertyToID("_VelocityScale"); 
 | 
            internal static readonly int _MaxBlurRadius     = Shader.PropertyToID("_MaxBlurRadius"); 
 | 
            internal static readonly int _RcpMaxBlurRadius  = Shader.PropertyToID("_RcpMaxBlurRadius"); 
 | 
            internal static readonly int _VelocityTex       = Shader.PropertyToID("_VelocityTex"); 
 | 
            internal static readonly int _MainTex           = Shader.PropertyToID("_MainTex"); 
 | 
            internal static readonly int _Tile2RT           = Shader.PropertyToID("_Tile2RT"); 
 | 
            internal static readonly int _Tile4RT           = Shader.PropertyToID("_Tile4RT"); 
 | 
            internal static readonly int _Tile8RT           = Shader.PropertyToID("_Tile8RT"); 
 | 
            internal static readonly int _TileMaxOffs       = Shader.PropertyToID("_TileMaxOffs"); 
 | 
            internal static readonly int _TileMaxLoop       = Shader.PropertyToID("_TileMaxLoop"); 
 | 
            internal static readonly int _TileVRT           = Shader.PropertyToID("_TileVRT"); 
 | 
            internal static readonly int _NeighborMaxTex    = Shader.PropertyToID("_NeighborMaxTex"); 
 | 
            internal static readonly int _LoopCount         = Shader.PropertyToID("_LoopCount"); 
 | 
            internal static readonly int _TempRT            = Shader.PropertyToID("_TempRT"); 
 | 
  
 | 
            internal static readonly int _History1LumaTex   = Shader.PropertyToID("_History1LumaTex"); 
 | 
            internal static readonly int _History2LumaTex   = Shader.PropertyToID("_History2LumaTex"); 
 | 
            internal static readonly int _History3LumaTex   = Shader.PropertyToID("_History3LumaTex"); 
 | 
            internal static readonly int _History4LumaTex   = Shader.PropertyToID("_History4LumaTex"); 
 | 
  
 | 
            internal static readonly int _History1ChromaTex = Shader.PropertyToID("_History1ChromaTex"); 
 | 
            internal static readonly int _History2ChromaTex = Shader.PropertyToID("_History2ChromaTex"); 
 | 
            internal static readonly int _History3ChromaTex = Shader.PropertyToID("_History3ChromaTex"); 
 | 
            internal static readonly int _History4ChromaTex = Shader.PropertyToID("_History4ChromaTex"); 
 | 
  
 | 
            internal static readonly int _History1Weight    = Shader.PropertyToID("_History1Weight"); 
 | 
            internal static readonly int _History2Weight    = Shader.PropertyToID("_History2Weight"); 
 | 
            internal static readonly int _History3Weight    = Shader.PropertyToID("_History3Weight"); 
 | 
            internal static readonly int _History4Weight    = Shader.PropertyToID("_History4Weight"); 
 | 
        } 
 | 
  
 | 
        enum Pass 
 | 
        { 
 | 
            VelocitySetup, 
 | 
            TileMax1, 
 | 
            TileMax2, 
 | 
            TileMaxV, 
 | 
            NeighborMax, 
 | 
            Reconstruction, 
 | 
            FrameCompression, 
 | 
            FrameBlendingChroma, 
 | 
            FrameBlendingRaw 
 | 
        } 
 | 
  
 | 
        public class ReconstructionFilter 
 | 
        { 
 | 
            // Texture format for storing 2D vectors. 
 | 
            RenderTextureFormat m_VectorRTFormat = RenderTextureFormat.RGHalf; 
 | 
  
 | 
            // Texture format for storing packed velocity/depth. 
 | 
            RenderTextureFormat m_PackedRTFormat = RenderTextureFormat.ARGB2101010; 
 | 
  
 | 
            public ReconstructionFilter() 
 | 
            { 
 | 
                CheckTextureFormatSupport(); 
 | 
            } 
 | 
  
 | 
            void CheckTextureFormatSupport() 
 | 
            { 
 | 
                // If 2:10:10:10 isn't supported, use ARGB32 instead. 
 | 
                if (!SystemInfo.SupportsRenderTextureFormat(m_PackedRTFormat)) 
 | 
                    m_PackedRTFormat = RenderTextureFormat.ARGB32; 
 | 
            } 
 | 
  
 | 
            public bool IsSupported() 
 | 
            { 
 | 
                return SystemInfo.supportsMotionVectors; 
 | 
            } 
 | 
  
 | 
            public void ProcessImage(PostProcessingContext context, CommandBuffer cb, ref Settings settings, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material) 
 | 
            { 
 | 
                const float kMaxBlurRadius = 5f; 
 | 
  
 | 
                // Calculate the maximum blur radius in pixels. 
 | 
                int maxBlurPixels = (int)(kMaxBlurRadius * context.height / 100); 
 | 
  
 | 
                // Calculate the TileMax size. 
 | 
                // It should be a multiple of 8 and larger than maxBlur. 
 | 
                int tileSize = ((maxBlurPixels - 1) / 8 + 1) * 8; 
 | 
  
 | 
                // Pass 1 - Velocity/depth packing 
 | 
                var velocityScale = settings.shutterAngle / 360f; 
 | 
                cb.SetGlobalFloat(Uniforms._VelocityScale, velocityScale); 
 | 
                cb.SetGlobalFloat(Uniforms._MaxBlurRadius, maxBlurPixels); 
 | 
                cb.SetGlobalFloat(Uniforms._RcpMaxBlurRadius, 1f / maxBlurPixels); 
 | 
  
 | 
                int vbuffer = Uniforms._VelocityTex; 
 | 
                cb.GetTemporaryRT(vbuffer, context.width, context.height, 0, FilterMode.Point, m_PackedRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.Blit((Texture)null, vbuffer, material, (int)Pass.VelocitySetup); 
 | 
  
 | 
                // Pass 2 - First TileMax filter (1/2 downsize) 
 | 
                int tile2 = Uniforms._Tile2RT; 
 | 
                cb.GetTemporaryRT(tile2, context.width / 2, context.height / 2, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, vbuffer); 
 | 
                cb.Blit(vbuffer, tile2, material, (int)Pass.TileMax1); 
 | 
  
 | 
                // Pass 3 - Second TileMax filter (1/2 downsize) 
 | 
                int tile4 = Uniforms._Tile4RT; 
 | 
                cb.GetTemporaryRT(tile4, context.width / 4, context.height / 4, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, tile2); 
 | 
                cb.Blit(tile2, tile4, material, (int)Pass.TileMax2); 
 | 
                cb.ReleaseTemporaryRT(tile2); 
 | 
  
 | 
                // Pass 4 - Third TileMax filter (1/2 downsize) 
 | 
                int tile8 = Uniforms._Tile8RT; 
 | 
                cb.GetTemporaryRT(tile8, context.width / 8, context.height / 8, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, tile4); 
 | 
                cb.Blit(tile4, tile8, material, (int)Pass.TileMax2); 
 | 
                cb.ReleaseTemporaryRT(tile4); 
 | 
  
 | 
                // Pass 5 - Fourth TileMax filter (reduce to tileSize) 
 | 
                var tileMaxOffs = Vector2.one * (tileSize / 8f - 1f) * -0.5f; 
 | 
                cb.SetGlobalVector(Uniforms._TileMaxOffs, tileMaxOffs); 
 | 
                cb.SetGlobalFloat(Uniforms._TileMaxLoop, (int)(tileSize / 8f)); 
 | 
  
 | 
                int tile = Uniforms._TileVRT; 
 | 
                cb.GetTemporaryRT(tile, context.width / tileSize, context.height / tileSize, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, tile8); 
 | 
                cb.Blit(tile8, tile, material, (int)Pass.TileMaxV); 
 | 
                cb.ReleaseTemporaryRT(tile8); 
 | 
  
 | 
                // Pass 6 - NeighborMax filter 
 | 
                int neighborMax = Uniforms._NeighborMaxTex; 
 | 
                int neighborMaxWidth = context.width / tileSize; 
 | 
                int neighborMaxHeight = context.height / tileSize; 
 | 
                cb.GetTemporaryRT(neighborMax, neighborMaxWidth, neighborMaxHeight, 0, FilterMode.Point, m_VectorRTFormat, RenderTextureReadWrite.Linear); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, tile); 
 | 
                cb.Blit(tile, neighborMax, material, (int)Pass.NeighborMax); 
 | 
                cb.ReleaseTemporaryRT(tile); 
 | 
  
 | 
                // Pass 7 - Reconstruction pass 
 | 
                cb.SetGlobalFloat(Uniforms._LoopCount, Mathf.Clamp(settings.sampleCount / 2, 1, 64)); 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, source); 
 | 
  
 | 
                cb.Blit(source, destination, material, (int)Pass.Reconstruction); 
 | 
  
 | 
                cb.ReleaseTemporaryRT(vbuffer); 
 | 
                cb.ReleaseTemporaryRT(neighborMax); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public class FrameBlendingFilter 
 | 
        { 
 | 
            struct Frame 
 | 
            { 
 | 
                public RenderTexture lumaTexture; 
 | 
                public RenderTexture chromaTexture; 
 | 
  
 | 
                float m_Time; 
 | 
                RenderTargetIdentifier[] m_MRT; 
 | 
  
 | 
                public float CalculateWeight(float strength, float currentTime) 
 | 
                { 
 | 
                    if (Mathf.Approximately(m_Time, 0f)) 
 | 
                        return 0f; 
 | 
  
 | 
                    var coeff = Mathf.Lerp(80f, 16f, strength); 
 | 
                    return Mathf.Exp((m_Time - currentTime) * coeff); 
 | 
                } 
 | 
  
 | 
                public void Release() 
 | 
                { 
 | 
                    if (lumaTexture != null) 
 | 
                        RenderTexture.ReleaseTemporary(lumaTexture); 
 | 
  
 | 
                    if (chromaTexture != null) 
 | 
                        RenderTexture.ReleaseTemporary(chromaTexture); 
 | 
  
 | 
                    lumaTexture = null; 
 | 
                    chromaTexture = null; 
 | 
                } 
 | 
  
 | 
                public void MakeRecord(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, Material material) 
 | 
                { 
 | 
                    Release(); 
 | 
  
 | 
                    lumaTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.R8, RenderTextureReadWrite.Linear); 
 | 
                    chromaTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.R8, RenderTextureReadWrite.Linear); 
 | 
  
 | 
                    lumaTexture.filterMode = FilterMode.Point; 
 | 
                    chromaTexture.filterMode = FilterMode.Point; 
 | 
  
 | 
                    if (m_MRT == null) 
 | 
                        m_MRT = new RenderTargetIdentifier[2]; 
 | 
  
 | 
                    m_MRT[0] = lumaTexture; 
 | 
                    m_MRT[1] = chromaTexture; 
 | 
  
 | 
                    cb.SetGlobalTexture(Uniforms._MainTex, source); 
 | 
                    cb.SetRenderTarget(m_MRT, lumaTexture); 
 | 
                    cb.DrawMesh(GraphicsUtils.quad, Matrix4x4.identity, material, 0, (int)Pass.FrameCompression); 
 | 
  
 | 
                    m_Time = Time.time; 
 | 
                } 
 | 
  
 | 
                public void MakeRecordRaw(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, RenderTextureFormat format) 
 | 
                { 
 | 
                    Release(); 
 | 
  
 | 
                    lumaTexture = RenderTexture.GetTemporary(width, height, 0, format); 
 | 
                    lumaTexture.filterMode = FilterMode.Point; 
 | 
  
 | 
                    cb.SetGlobalTexture(Uniforms._MainTex, source); 
 | 
                    cb.Blit(source, lumaTexture); 
 | 
  
 | 
                    m_Time = Time.time; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            bool m_UseCompression; 
 | 
            RenderTextureFormat m_RawTextureFormat; 
 | 
  
 | 
            Frame[] m_FrameList; 
 | 
            int m_LastFrameCount; 
 | 
  
 | 
            public FrameBlendingFilter() 
 | 
            { 
 | 
                m_UseCompression = CheckSupportCompression(); 
 | 
                m_RawTextureFormat = GetPreferredRenderTextureFormat(); 
 | 
                m_FrameList = new Frame[4]; 
 | 
            } 
 | 
  
 | 
            public void Dispose() 
 | 
            { 
 | 
                foreach (var frame in m_FrameList) 
 | 
                    frame.Release(); 
 | 
            } 
 | 
  
 | 
            public void PushFrame(CommandBuffer cb, RenderTargetIdentifier source, int width, int height, Material material) 
 | 
            { 
 | 
                // Push only when actual update (do nothing while pausing) 
 | 
                var frameCount = Time.frameCount; 
 | 
                if (frameCount == m_LastFrameCount) return; 
 | 
  
 | 
                // Update the frame record. 
 | 
                var index = frameCount % m_FrameList.Length; 
 | 
  
 | 
                if (m_UseCompression) 
 | 
                    m_FrameList[index].MakeRecord(cb, source, width, height, material); 
 | 
                else 
 | 
                    m_FrameList[index].MakeRecordRaw(cb, source, width, height, m_RawTextureFormat); 
 | 
  
 | 
                m_LastFrameCount = frameCount; 
 | 
            } 
 | 
  
 | 
            public void BlendFrames(CommandBuffer cb, float strength, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material) 
 | 
            { 
 | 
                var t = Time.time; 
 | 
  
 | 
                var f1 = GetFrameRelative(-1); 
 | 
                var f2 = GetFrameRelative(-2); 
 | 
                var f3 = GetFrameRelative(-3); 
 | 
                var f4 = GetFrameRelative(-4); 
 | 
  
 | 
                cb.SetGlobalTexture(Uniforms._History1LumaTex, f1.lumaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History2LumaTex, f2.lumaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History3LumaTex, f3.lumaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History4LumaTex, f4.lumaTexture); 
 | 
  
 | 
                cb.SetGlobalTexture(Uniforms._History1ChromaTex, f1.chromaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History2ChromaTex, f2.chromaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History3ChromaTex, f3.chromaTexture); 
 | 
                cb.SetGlobalTexture(Uniforms._History4ChromaTex, f4.chromaTexture); 
 | 
  
 | 
                cb.SetGlobalFloat(Uniforms._History1Weight, f1.CalculateWeight(strength, t)); 
 | 
                cb.SetGlobalFloat(Uniforms._History2Weight, f2.CalculateWeight(strength, t)); 
 | 
                cb.SetGlobalFloat(Uniforms._History3Weight, f3.CalculateWeight(strength, t)); 
 | 
                cb.SetGlobalFloat(Uniforms._History4Weight, f4.CalculateWeight(strength, t)); 
 | 
  
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, source); 
 | 
                cb.Blit(source, destination, material, m_UseCompression ? (int)Pass.FrameBlendingChroma : (int)Pass.FrameBlendingRaw); 
 | 
            } 
 | 
  
 | 
            // Check if the platform has the capability of compression. 
 | 
            static bool CheckSupportCompression() 
 | 
            { 
 | 
                return 
 | 
                    SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8) && 
 | 
                    SystemInfo.supportedRenderTargetCount > 1; 
 | 
            } 
 | 
  
 | 
            // Determine which 16-bit render texture format is available. 
 | 
            static RenderTextureFormat GetPreferredRenderTextureFormat() 
 | 
            { 
 | 
                RenderTextureFormat[] formats = 
 | 
                { 
 | 
                    RenderTextureFormat.RGB565, 
 | 
                    RenderTextureFormat.ARGB1555, 
 | 
                    RenderTextureFormat.ARGB4444 
 | 
                }; 
 | 
  
 | 
                foreach (var f in formats) 
 | 
                    if (SystemInfo.SupportsRenderTextureFormat(f)) return f; 
 | 
  
 | 
                return RenderTextureFormat.Default; 
 | 
            } 
 | 
  
 | 
            // Retrieve a frame record with relative indexing. 
 | 
            // Use a negative index to refer to previous frames. 
 | 
            Frame GetFrameRelative(int offset) 
 | 
            { 
 | 
                var index = (Time.frameCount + m_FrameList.Length + offset) % m_FrameList.Length; 
 | 
                return m_FrameList[index]; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        ReconstructionFilter m_ReconstructionFilter; 
 | 
        public ReconstructionFilter reconstructionFilter 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                if (m_ReconstructionFilter == null) 
 | 
                    m_ReconstructionFilter = new ReconstructionFilter(); 
 | 
  
 | 
                return m_ReconstructionFilter; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        FrameBlendingFilter m_FrameBlendingFilter; 
 | 
        public FrameBlendingFilter frameBlendingFilter 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                if (m_FrameBlendingFilter == null) 
 | 
                    m_FrameBlendingFilter = new FrameBlendingFilter(); 
 | 
  
 | 
                return m_FrameBlendingFilter; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        bool m_FirstFrame = true; 
 | 
  
 | 
        public override bool active 
 | 
        { 
 | 
            get 
 | 
            { 
 | 
                var settings = model.settings; 
 | 
                return model.enabled 
 | 
                       && ((settings.shutterAngle > 0f && reconstructionFilter.IsSupported()) || settings.frameBlending > 0f) 
 | 
                       && SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2 // No movecs on GLES2 platforms 
 | 
                       && !context.interrupted; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        public override string GetName() 
 | 
        { 
 | 
            return "Motion Blur"; 
 | 
        } 
 | 
  
 | 
        public void ResetHistory() 
 | 
        { 
 | 
            if (m_FrameBlendingFilter != null) 
 | 
                m_FrameBlendingFilter.Dispose(); 
 | 
  
 | 
            m_FrameBlendingFilter = null; 
 | 
        } 
 | 
  
 | 
        public override DepthTextureMode GetCameraFlags() 
 | 
        { 
 | 
            return DepthTextureMode.Depth | DepthTextureMode.MotionVectors; 
 | 
        } 
 | 
  
 | 
        public override CameraEvent GetCameraEvent() 
 | 
        { 
 | 
            return CameraEvent.BeforeImageEffects; 
 | 
        } 
 | 
  
 | 
        public override void OnEnable() 
 | 
        { 
 | 
            m_FirstFrame = true; 
 | 
        } 
 | 
  
 | 
        public override void PopulateCommandBuffer(CommandBuffer cb) 
 | 
        { 
 | 
#if UNITY_EDITOR 
 | 
            // Don't render motion blur preview when the editor is not playing as it can in some 
 | 
            // cases results in ugly artifacts (i.e. when resizing the game view). 
 | 
            if (!Application.isPlaying) 
 | 
                return; 
 | 
#endif 
 | 
  
 | 
            // Skip rendering in the first frame as motion vectors won't be abvailable until the 
 | 
            // next one 
 | 
            if (m_FirstFrame) 
 | 
            { 
 | 
                m_FirstFrame = false; 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            var material = context.materialFactory.Get("Hidden/Post FX/Motion Blur"); 
 | 
            var blitMaterial = context.materialFactory.Get("Hidden/Post FX/Blit"); 
 | 
            var settings = model.settings; 
 | 
  
 | 
            var fbFormat = context.isHdr 
 | 
                ? RenderTextureFormat.DefaultHDR 
 | 
                : RenderTextureFormat.Default; 
 | 
  
 | 
            int tempRT = Uniforms._TempRT; 
 | 
            cb.GetTemporaryRT(tempRT, context.width, context.height, 0, FilterMode.Point, fbFormat); 
 | 
  
 | 
            if (settings.shutterAngle > 0f && settings.frameBlending > 0f) 
 | 
            { 
 | 
                // Motion blur + frame blending 
 | 
                reconstructionFilter.ProcessImage(context, cb, ref settings, BuiltinRenderTextureType.CameraTarget, tempRT, material); 
 | 
                frameBlendingFilter.BlendFrames(cb, settings.frameBlending, tempRT, BuiltinRenderTextureType.CameraTarget, material); 
 | 
                frameBlendingFilter.PushFrame(cb, tempRT, context.width, context.height, material); 
 | 
            } 
 | 
            else if (settings.shutterAngle > 0f) 
 | 
            { 
 | 
                // No frame blending 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, BuiltinRenderTextureType.CameraTarget); 
 | 
                cb.Blit(BuiltinRenderTextureType.CameraTarget, tempRT, blitMaterial, 0); 
 | 
                reconstructionFilter.ProcessImage(context, cb, ref settings, tempRT, BuiltinRenderTextureType.CameraTarget, material); 
 | 
            } 
 | 
            else if (settings.frameBlending > 0f) 
 | 
            { 
 | 
                // Frame blending only 
 | 
                cb.SetGlobalTexture(Uniforms._MainTex, BuiltinRenderTextureType.CameraTarget); 
 | 
                cb.Blit(BuiltinRenderTextureType.CameraTarget, tempRT, blitMaterial, 0); 
 | 
                frameBlendingFilter.BlendFrames(cb, settings.frameBlending, tempRT, BuiltinRenderTextureType.CameraTarget, material); 
 | 
                frameBlendingFilter.PushFrame(cb, tempRT, context.width, context.height, material); 
 | 
            } 
 | 
  
 | 
            // Cleaning up 
 | 
            cb.ReleaseTemporaryRT(tempRT); 
 | 
        } 
 | 
  
 | 
        public override void OnDisable() 
 | 
        { 
 | 
            if (m_FrameBlendingFilter != null) 
 | 
                m_FrameBlendingFilter.Dispose(); 
 | 
        } 
 | 
    } 
 | 
} 
 |