using UnityEngine; namespace UnityStandardAssets.ImageEffects { [ExecuteInEditMode] [RequireComponent(typeof(Camera))] [AddComponentMenu("Image Effects/Camera/Camera Motion Blur")] public class CameraMotionBlur : PostEffectsBase { public enum MotionBlurFilter { CameraMotion = 0, LocalBlur = 1, Reconstruction = 2, ReconstructionDX11 = 3, ReconstructionDisc = 4 } private static float MAX_RADIUS = 10f; public MotionBlurFilter filterType = MotionBlurFilter.Reconstruction; public bool preview; public Vector3 previewScale = Vector3.one; public float movementScale; public float rotationScale = 1f; public float maxVelocity = 8f; public float minVelocity = 0.1f; public float velocityScale = 0.375f; public float softZDistance = 0.005f; public int velocityDownsample = 1; public LayerMask excludeLayers = 0; private GameObject tmpCam; public Shader shader; public Shader dx11MotionBlurShader; public Shader replacementClear; private Material motionBlurMaterial; private Material dx11MotionBlurMaterial; public Texture2D noiseTexture; public float jitter = 0.05f; public bool showVelocity; public float showVelocityScale = 1f; private Matrix4x4 currentViewProjMat; private Matrix4x4 prevViewProjMat; private int prevFrameCount; private bool wasActive; private Vector3 prevFrameForward = Vector3.forward; private Vector3 prevFrameUp = Vector3.up; private Vector3 prevFramePos = Vector3.zero; private Camera _camera; private void CalculateViewProjection() { Matrix4x4 worldToCameraMatrix = _camera.worldToCameraMatrix; Matrix4x4 gPUProjectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, true); currentViewProjMat = gPUProjectionMatrix * worldToCameraMatrix; } private new void Start() { CheckResources(); if (_camera == null) { _camera = GetComponent(); } wasActive = base.gameObject.activeInHierarchy; CalculateViewProjection(); Remember(); wasActive = false; } private void OnEnable() { if (_camera == null) { _camera = GetComponent(); } _camera.depthTextureMode |= DepthTextureMode.Depth; } private void OnDisable() { if (null != motionBlurMaterial) { Object.DestroyImmediate(motionBlurMaterial); motionBlurMaterial = null; } if (null != dx11MotionBlurMaterial) { Object.DestroyImmediate(dx11MotionBlurMaterial); dx11MotionBlurMaterial = null; } if (null != tmpCam) { Object.DestroyImmediate(tmpCam); tmpCam = null; } } public override bool CheckResources() { CheckSupport(true, true); motionBlurMaterial = CheckShaderAndCreateMaterial(shader, motionBlurMaterial); if (supportDX11 && filterType == MotionBlurFilter.ReconstructionDX11) { dx11MotionBlurMaterial = CheckShaderAndCreateMaterial(dx11MotionBlurShader, dx11MotionBlurMaterial); } if (!isSupported) { ReportAutoDisable(); } return isSupported; } private void OnRenderImage(RenderTexture source, RenderTexture destination) { if (!CheckResources()) { Graphics.Blit(source, destination); return; } if (filterType == MotionBlurFilter.CameraMotion) { StartFrame(); } RenderTextureFormat format = ((!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf)) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.RGHalf); RenderTexture temporary = RenderTexture.GetTemporary(divRoundUp(source.width, velocityDownsample), divRoundUp(source.height, velocityDownsample), 0, format); int num = 1; int num2 = 1; maxVelocity = Mathf.Max(2f, maxVelocity); float num3 = maxVelocity; bool flag = filterType == MotionBlurFilter.ReconstructionDX11 && dx11MotionBlurMaterial == null; if (filterType == MotionBlurFilter.Reconstruction || flag || filterType == MotionBlurFilter.ReconstructionDisc) { maxVelocity = Mathf.Min(maxVelocity, MAX_RADIUS); num = divRoundUp(temporary.width, (int)maxVelocity); num2 = divRoundUp(temporary.height, (int)maxVelocity); num3 = temporary.width / num; } else { num = divRoundUp(temporary.width, (int)maxVelocity); num2 = divRoundUp(temporary.height, (int)maxVelocity); num3 = temporary.width / num; } RenderTexture temporary2 = RenderTexture.GetTemporary(num, num2, 0, format); RenderTexture temporary3 = RenderTexture.GetTemporary(num, num2, 0, format); temporary.filterMode = FilterMode.Point; temporary2.filterMode = FilterMode.Point; temporary3.filterMode = FilterMode.Point; if ((bool)noiseTexture) { noiseTexture.filterMode = FilterMode.Point; } source.wrapMode = TextureWrapMode.Clamp; temporary.wrapMode = TextureWrapMode.Clamp; temporary3.wrapMode = TextureWrapMode.Clamp; temporary2.wrapMode = TextureWrapMode.Clamp; CalculateViewProjection(); if (base.gameObject.activeInHierarchy && !wasActive) { Remember(); } wasActive = base.gameObject.activeInHierarchy; Matrix4x4 matrix4x = Matrix4x4.Inverse(currentViewProjMat); motionBlurMaterial.SetMatrix("_InvViewProj", matrix4x); motionBlurMaterial.SetMatrix("_PrevViewProj", prevViewProjMat); motionBlurMaterial.SetMatrix("_ToPrevViewProjCombined", prevViewProjMat * matrix4x); motionBlurMaterial.SetFloat("_MaxVelocity", num3); motionBlurMaterial.SetFloat("_MaxRadiusOrKInPaper", num3); motionBlurMaterial.SetFloat("_MinVelocity", minVelocity); motionBlurMaterial.SetFloat("_VelocityScale", velocityScale); motionBlurMaterial.SetFloat("_Jitter", jitter); motionBlurMaterial.SetTexture("_NoiseTex", noiseTexture); motionBlurMaterial.SetTexture("_VelTex", temporary); motionBlurMaterial.SetTexture("_NeighbourMaxTex", temporary3); motionBlurMaterial.SetTexture("_TileTexDebug", temporary2); if (preview) { Matrix4x4 worldToCameraMatrix = _camera.worldToCameraMatrix; Matrix4x4 identity = Matrix4x4.identity; identity.SetTRS(previewScale * 0.3333f, Quaternion.identity, Vector3.one); Matrix4x4 gPUProjectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, true); prevViewProjMat = gPUProjectionMatrix * identity * worldToCameraMatrix; motionBlurMaterial.SetMatrix("_PrevViewProj", prevViewProjMat); motionBlurMaterial.SetMatrix("_ToPrevViewProjCombined", prevViewProjMat * matrix4x); } if (filterType == MotionBlurFilter.CameraMotion) { Vector4 zero = Vector4.zero; float num4 = Vector3.Dot(base.transform.up, Vector3.up); Vector3 rhs = prevFramePos - base.transform.position; float magnitude = rhs.magnitude; float num5 = 1f; num5 = Vector3.Angle(base.transform.up, prevFrameUp) / _camera.fieldOfView * ((float)source.width * 0.75f); zero.x = rotationScale * num5; num5 = Vector3.Angle(base.transform.forward, prevFrameForward) / _camera.fieldOfView * ((float)source.width * 0.75f); zero.y = rotationScale * num4 * num5; num5 = Vector3.Angle(base.transform.forward, prevFrameForward) / _camera.fieldOfView * ((float)source.width * 0.75f); zero.z = rotationScale * (1f - num4) * num5; if (magnitude > Mathf.Epsilon && movementScale > Mathf.Epsilon) { zero.w = movementScale * Vector3.Dot(base.transform.forward, rhs) * ((float)source.width * 0.5f); zero.x += movementScale * Vector3.Dot(base.transform.up, rhs) * ((float)source.width * 0.5f); zero.y += movementScale * Vector3.Dot(base.transform.right, rhs) * ((float)source.width * 0.5f); } if (preview) { motionBlurMaterial.SetVector("_BlurDirectionPacked", new Vector4(previewScale.y, previewScale.x, 0f, previewScale.z) * 0.5f * _camera.fieldOfView); } else { motionBlurMaterial.SetVector("_BlurDirectionPacked", zero); } } else { Graphics.Blit(source, temporary, motionBlurMaterial, 0); Camera camera = null; if (excludeLayers.value != 0) { camera = GetTmpCam(); } if ((bool)camera && excludeLayers.value != 0 && (bool)replacementClear && replacementClear.isSupported) { camera.targetTexture = temporary; camera.cullingMask = excludeLayers; camera.RenderWithShader(replacementClear, string.Empty); } } if (!preview && Time.frameCount != prevFrameCount) { prevFrameCount = Time.frameCount; Remember(); } source.filterMode = FilterMode.Bilinear; if (showVelocity) { motionBlurMaterial.SetFloat("_DisplayVelocityScale", showVelocityScale); Graphics.Blit(temporary, destination, motionBlurMaterial, 1); } else if (filterType == MotionBlurFilter.ReconstructionDX11 && !flag) { dx11MotionBlurMaterial.SetFloat("_MinVelocity", minVelocity); dx11MotionBlurMaterial.SetFloat("_VelocityScale", velocityScale); dx11MotionBlurMaterial.SetFloat("_Jitter", jitter); dx11MotionBlurMaterial.SetTexture("_NoiseTex", noiseTexture); dx11MotionBlurMaterial.SetTexture("_VelTex", temporary); dx11MotionBlurMaterial.SetTexture("_NeighbourMaxTex", temporary3); dx11MotionBlurMaterial.SetFloat("_SoftZDistance", Mathf.Max(0.00025f, softZDistance)); dx11MotionBlurMaterial.SetFloat("_MaxRadiusOrKInPaper", num3); Graphics.Blit(temporary, temporary2, dx11MotionBlurMaterial, 0); Graphics.Blit(temporary2, temporary3, dx11MotionBlurMaterial, 1); Graphics.Blit(source, destination, dx11MotionBlurMaterial, 2); } else if (filterType == MotionBlurFilter.Reconstruction || flag) { motionBlurMaterial.SetFloat("_SoftZDistance", Mathf.Max(0.00025f, softZDistance)); Graphics.Blit(temporary, temporary2, motionBlurMaterial, 2); Graphics.Blit(temporary2, temporary3, motionBlurMaterial, 3); Graphics.Blit(source, destination, motionBlurMaterial, 4); } else if (filterType == MotionBlurFilter.CameraMotion) { Graphics.Blit(source, destination, motionBlurMaterial, 6); } else if (filterType == MotionBlurFilter.ReconstructionDisc) { motionBlurMaterial.SetFloat("_SoftZDistance", Mathf.Max(0.00025f, softZDistance)); Graphics.Blit(temporary, temporary2, motionBlurMaterial, 2); Graphics.Blit(temporary2, temporary3, motionBlurMaterial, 3); Graphics.Blit(source, destination, motionBlurMaterial, 7); } else { Graphics.Blit(source, destination, motionBlurMaterial, 5); } RenderTexture.ReleaseTemporary(temporary); RenderTexture.ReleaseTemporary(temporary2); RenderTexture.ReleaseTemporary(temporary3); } private void Remember() { prevViewProjMat = currentViewProjMat; prevFrameForward = base.transform.forward; prevFrameUp = base.transform.up; prevFramePos = base.transform.position; } private Camera GetTmpCam() { if (tmpCam == null) { string text = "_" + _camera.name + "_MotionBlurTmpCam"; GameObject gameObject = GameObject.Find(text); if (null == gameObject) { tmpCam = new GameObject(text, typeof(Camera)); } else { tmpCam = gameObject; } } tmpCam.hideFlags = HideFlags.DontSave; tmpCam.transform.position = _camera.transform.position; tmpCam.transform.rotation = _camera.transform.rotation; tmpCam.transform.localScale = _camera.transform.localScale; tmpCam.GetComponent().CopyFrom(_camera); tmpCam.GetComponent().enabled = false; tmpCam.GetComponent().depthTextureMode = DepthTextureMode.None; tmpCam.GetComponent().clearFlags = CameraClearFlags.Nothing; return tmpCam.GetComponent(); } private void StartFrame() { prevFramePos = Vector3.Slerp(prevFramePos, base.transform.position, 0.75f); } private static int divRoundUp(int x, int d) { return (x + d - 1) / d; } } }