Files
2026-02-21 16:45:37 +08:00

377 lines
11 KiB
C#

using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityStandardAssets.CinematicEffects
{
[RequireComponent(typeof(Camera))]
[AddComponentMenu("Image Effects/Cinematic/Motion Blur")]
public class MotionBlur : MonoBehaviour
{
private class FrameBlendingFilter
{
private struct Frame
{
public RenderTexture lumaTexture;
public RenderTexture chromaTexture;
public float time;
private RenderBuffer[] _mrt;
public float CalculateWeight(float strength, float currentTime)
{
if (time == 0f)
{
return 0f;
}
float num = Mathf.Lerp(80f, 16f, strength);
return Mathf.Exp((time - currentTime) * num);
}
public void Release()
{
if (lumaTexture != null)
{
RenderTexture.ReleaseTemporary(lumaTexture);
}
if (chromaTexture != null)
{
RenderTexture.ReleaseTemporary(chromaTexture);
}
lumaTexture = null;
chromaTexture = null;
}
public void MakeRecord(RenderTexture source, Material material)
{
Release();
lumaTexture = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.R8);
chromaTexture = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.R8);
lumaTexture.filterMode = FilterMode.Point;
chromaTexture.filterMode = FilterMode.Point;
if (_mrt == null)
{
_mrt = new RenderBuffer[2];
}
_mrt[0] = lumaTexture.colorBuffer;
_mrt[1] = chromaTexture.colorBuffer;
Graphics.SetRenderTarget(_mrt, lumaTexture.depthBuffer);
Graphics.Blit(source, material, 0);
time = Time.time;
}
public void MakeRecordRaw(RenderTexture source, RenderTextureFormat format)
{
Release();
lumaTexture = RenderTexture.GetTemporary(source.width, source.height, 0, format);
lumaTexture.filterMode = FilterMode.Point;
Graphics.Blit(source, lumaTexture);
time = Time.time;
}
}
private bool _useCompression;
private RenderTextureFormat _rawTextureFormat;
private Material _material;
private Frame[] _frameList;
private int _lastFrameCount;
public FrameBlendingFilter()
{
_useCompression = CheckSupportCompression();
_rawTextureFormat = GetPreferredRenderTextureFormat();
_material = new Material(Shader.Find("Hidden/Image Effects/Cinematic/MotionBlur/FrameBlending"));
_material.hideFlags = HideFlags.DontSave;
_frameList = new Frame[4];
}
public void Release()
{
UnityEngine.Object.DestroyImmediate(_material);
_material = null;
Frame[] frameList = _frameList;
foreach (Frame frame in frameList)
{
frame.Release();
}
_frameList = null;
}
public void PushFrame(RenderTexture source)
{
int frameCount = Time.frameCount;
if (frameCount != _lastFrameCount)
{
int num = frameCount % _frameList.Length;
if (_useCompression)
{
_frameList[num].MakeRecord(source, _material);
}
else
{
_frameList[num].MakeRecordRaw(source, _rawTextureFormat);
}
_lastFrameCount = frameCount;
}
}
public void BlendFrames(float strength, RenderTexture source, RenderTexture destination)
{
float time = Time.time;
Frame frameRelative = GetFrameRelative(-1);
Frame frameRelative2 = GetFrameRelative(-2);
Frame frameRelative3 = GetFrameRelative(-3);
Frame frameRelative4 = GetFrameRelative(-4);
_material.SetTexture("_History1LumaTex", frameRelative.lumaTexture);
_material.SetTexture("_History2LumaTex", frameRelative2.lumaTexture);
_material.SetTexture("_History3LumaTex", frameRelative3.lumaTexture);
_material.SetTexture("_History4LumaTex", frameRelative4.lumaTexture);
_material.SetTexture("_History1ChromaTex", frameRelative.chromaTexture);
_material.SetTexture("_History2ChromaTex", frameRelative2.chromaTexture);
_material.SetTexture("_History3ChromaTex", frameRelative3.chromaTexture);
_material.SetTexture("_History4ChromaTex", frameRelative4.chromaTexture);
_material.SetFloat("_History1Weight", frameRelative.CalculateWeight(strength, time));
_material.SetFloat("_History2Weight", frameRelative2.CalculateWeight(strength, time));
_material.SetFloat("_History3Weight", frameRelative3.CalculateWeight(strength, time));
_material.SetFloat("_History4Weight", frameRelative4.CalculateWeight(strength, time));
Graphics.Blit(source, destination, _material, _useCompression ? 1 : 2);
}
private static bool CheckSupportCompression()
{
return SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2 && SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8) && SystemInfo.supportedRenderTargetCount > 1;
}
private static RenderTextureFormat GetPreferredRenderTextureFormat()
{
RenderTextureFormat[] array = new RenderTextureFormat[3]
{
RenderTextureFormat.RGB565,
RenderTextureFormat.ARGB1555,
RenderTextureFormat.ARGB4444
};
RenderTextureFormat[] array2 = array;
foreach (RenderTextureFormat renderTextureFormat in array2)
{
if (SystemInfo.SupportsRenderTextureFormat(renderTextureFormat))
{
return renderTextureFormat;
}
}
return RenderTextureFormat.Default;
}
private Frame GetFrameRelative(int offset)
{
int num = (Time.frameCount + _frameList.Length + offset) % _frameList.Length;
return _frameList[num];
}
}
private class ReconstructionFilter
{
private const float kMaxBlurRadius = 5f;
private Material _material;
private bool _unroll;
private RenderTextureFormat _vectorRTFormat = RenderTextureFormat.RGHalf;
private RenderTextureFormat _packedRTFormat = RenderTextureFormat.ARGB2101010;
public ReconstructionFilter()
{
Shader shader = Shader.Find("Hidden/Image Effects/Cinematic/MotionBlur/Reconstruction");
if (shader.isSupported && CheckTextureFormatSupport())
{
_material = new Material(shader);
_material.hideFlags = HideFlags.DontSave;
}
_unroll = SystemInfo.graphicsDeviceName.Contains("Adreno");
}
public void Release()
{
if (_material != null)
{
UnityEngine.Object.DestroyImmediate(_material);
}
_material = null;
}
public void ProcessImage(float shutterAngle, int sampleCount, RenderTexture source, RenderTexture destination)
{
if (_material == null)
{
Graphics.Blit(source, destination);
return;
}
int num = (int)(5f * (float)source.height / 100f);
int num2 = ((num - 1) / 8 + 1) * 8;
float value = shutterAngle / 360f * 1.45f;
_material.SetFloat("_VelocityScale", value);
_material.SetFloat("_MaxBlurRadius", num);
RenderTexture temporaryRT = GetTemporaryRT(source, 1, _packedRTFormat);
Graphics.Blit(null, temporaryRT, _material, 0);
RenderTexture temporaryRT2 = GetTemporaryRT(source, 4, _vectorRTFormat);
Graphics.Blit(temporaryRT, temporaryRT2, _material, 1);
RenderTexture temporaryRT3 = GetTemporaryRT(source, 8, _vectorRTFormat);
Graphics.Blit(temporaryRT2, temporaryRT3, _material, 2);
ReleaseTemporaryRT(temporaryRT2);
Vector2 vector = Vector2.one * ((float)num2 / 8f - 1f) * -0.5f;
_material.SetVector("_TileMaxOffs", vector);
_material.SetInt("_TileMaxLoop", num2 / 8);
RenderTexture temporaryRT4 = GetTemporaryRT(source, num2, _vectorRTFormat);
Graphics.Blit(temporaryRT3, temporaryRT4, _material, 3);
ReleaseTemporaryRT(temporaryRT3);
RenderTexture temporaryRT5 = GetTemporaryRT(source, num2, _vectorRTFormat);
Graphics.Blit(temporaryRT4, temporaryRT5, _material, 4);
ReleaseTemporaryRT(temporaryRT4);
_material.SetInt("_LoopCount", Mathf.Clamp(sampleCount / 2, 1, 64));
_material.SetFloat("_MaxBlurRadius", num);
_material.SetTexture("_NeighborMaxTex", temporaryRT5);
_material.SetTexture("_VelocityTex", temporaryRT);
Graphics.Blit(source, destination, _material, (!_unroll) ? 5 : 6);
ReleaseTemporaryRT(temporaryRT);
ReleaseTemporaryRT(temporaryRT5);
}
private bool CheckTextureFormatSupport()
{
if (!SystemInfo.SupportsRenderTextureFormat(_vectorRTFormat))
{
return false;
}
if (!SystemInfo.SupportsRenderTextureFormat(_packedRTFormat))
{
_packedRTFormat = RenderTextureFormat.ARGB32;
}
return true;
}
private RenderTexture GetTemporaryRT(Texture source, int divider, RenderTextureFormat format)
{
int width = source.width / divider;
int height = source.height / divider;
RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, format);
temporary.filterMode = FilterMode.Point;
return temporary;
}
private void ReleaseTemporaryRT(RenderTexture rt)
{
RenderTexture.ReleaseTemporary(rt);
}
}
[Serializable]
public class Settings
{
[SerializeField]
[Range(0f, 360f)]
[Tooltip("The angle of rotary shutter. Larger values give longer exposure.")]
public float shutterAngle;
[SerializeField]
[Tooltip("The amount of sample points, which affects quality and performance.")]
public int sampleCount;
[SerializeField]
[Range(0f, 1f)]
[Tooltip("The strength of multiple frame blending")]
public float frameBlending;
public static Settings defaultSettings
{
get
{
Settings settings = new Settings();
settings.shutterAngle = 270f;
settings.sampleCount = 10;
settings.frameBlending = 0f;
return settings;
}
}
}
[SerializeField]
private Settings _settings = Settings.defaultSettings;
[SerializeField]
private Shader _reconstructionShader;
[SerializeField]
private Shader _frameBlendingShader;
private ReconstructionFilter _reconstructionFilter;
private FrameBlendingFilter _frameBlendingFilter;
public Settings settings
{
get
{
return _settings;
}
set
{
_settings = value;
}
}
private void OnEnable()
{
_reconstructionFilter = new ReconstructionFilter();
_frameBlendingFilter = new FrameBlendingFilter();
}
private void OnDisable()
{
_reconstructionFilter.Release();
_frameBlendingFilter.Release();
_reconstructionFilter = null;
_frameBlendingFilter = null;
}
private void Update()
{
if (_settings.shutterAngle > 0f)
{
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (_settings.shutterAngle > 0f && _settings.frameBlending > 0f)
{
RenderTexture temporary = RenderTexture.GetTemporary(source.width, source.height, 0, source.format);
_reconstructionFilter.ProcessImage(_settings.shutterAngle, _settings.sampleCount, source, temporary);
_frameBlendingFilter.BlendFrames(_settings.frameBlending, temporary, destination);
_frameBlendingFilter.PushFrame(temporary);
RenderTexture.ReleaseTemporary(temporary);
}
else if (_settings.shutterAngle > 0f)
{
_reconstructionFilter.ProcessImage(_settings.shutterAngle, _settings.sampleCount, source, destination);
}
else if (_settings.frameBlending > 0f)
{
_frameBlendingFilter.BlendFrames(_settings.frameBlending, source, destination);
_frameBlendingFilter.PushFrame(source);
}
else
{
Graphics.Blit(source, destination);
}
}
}
}