377 lines
11 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|