MotionBlur.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. using UnityEngine;
  2. //-----------------------------------------------------------------------------
  3. // Copyright 2012-2017 RenderHeads Ltd. All rights reserved.
  4. //-----------------------------------------------------------------------------
  5. namespace RenderHeads.Media.AVProMovieCapture
  6. {
  7. /// <summary>
  8. /// Accumulates frames to average for generating motion blur in renders
  9. /// </summary>
  10. [AddComponentMenu("AVPro Movie Capture/Motion Blur", 301)]
  11. public class MotionBlur : MonoBehaviour
  12. {
  13. [SerializeField]
  14. public RenderTextureFormat _format = RenderTextureFormat.ARGBFloat;
  15. [SerializeField]
  16. private int _numSamples = 16;
  17. // State
  18. private RenderTexture _accum;
  19. private RenderTexture _lastComp;
  20. private Material _addMaterial;
  21. private Material _divMaterial;
  22. private int _frameCount;
  23. private int _targetWidth;
  24. private int _targetHeight;
  25. private bool _isDirty;
  26. private static int _propNumSamples;
  27. private static int _propWeight;
  28. public bool IsFrameAccumulated
  29. {
  30. get;
  31. private set;
  32. }
  33. public int NumSamples
  34. {
  35. get { return _numSamples; }
  36. set { _numSamples = value; OnNumSamplesChanged(); }
  37. }
  38. public int FrameCount
  39. {
  40. get { return _frameCount; }
  41. }
  42. public RenderTexture FinalTexture
  43. {
  44. get { return _lastComp; }
  45. }
  46. private void Awake()
  47. {
  48. if (_propNumSamples == 0)
  49. {
  50. _propNumSamples = Shader.PropertyToID("_NumSamples");
  51. _propWeight = Shader.PropertyToID("_Weight");
  52. }
  53. }
  54. public void SetTargetSize(int width, int height)
  55. {
  56. if (_targetWidth != width || _targetHeight != height)
  57. {
  58. _targetWidth = width;
  59. _targetHeight = height;
  60. _isDirty = true;
  61. }
  62. }
  63. private void Start()
  64. {
  65. Setup();
  66. }
  67. private void OnEnable()
  68. {
  69. _frameCount = 0;
  70. IsFrameAccumulated = false;
  71. ClearAccumulation();
  72. }
  73. private void Setup()
  74. {
  75. if (_addMaterial == null)
  76. {
  77. Shader addShader = Resources.Load<Shader>("AVProMovieCapture_MotionBlur_Add");
  78. Shader divShader = Resources.Load<Shader>("AVProMovieCapture_MotionBlur_Div");
  79. _addMaterial = new Material(addShader);
  80. _divMaterial = new Material(divShader);
  81. }
  82. if (_targetWidth == 0 && _targetHeight == 0)
  83. {
  84. // TODO: change the size of these RenderTextures based on the output size of the capture
  85. _targetWidth = Screen.width;
  86. _targetHeight = Screen.height;
  87. }
  88. if (_accum != null)
  89. {
  90. _accum.Release();
  91. RenderTexture.Destroy(_accum);
  92. _accum = null;
  93. }
  94. if (_lastComp != null)
  95. {
  96. _lastComp.Release();
  97. RenderTexture.Destroy(_lastComp);
  98. _lastComp = null;
  99. }
  100. _accum = new RenderTexture(_targetWidth, _targetHeight, 0, _format, RenderTextureReadWrite.Default);
  101. _lastComp = new RenderTexture(_targetWidth, _targetHeight, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
  102. _accum.filterMode = FilterMode.Point;
  103. _lastComp.filterMode = FilterMode.Bilinear;
  104. _accum.Create();
  105. _lastComp.Create();
  106. OnNumSamplesChanged();
  107. _isDirty = false;
  108. }
  109. private void ClearAccumulation()
  110. {
  111. RenderTexture prev = RenderTexture.active;
  112. RenderTexture.active = _accum;
  113. GL.Clear(false, true, Color.black);
  114. RenderTexture.active = prev;
  115. }
  116. private void OnDestroy()
  117. {
  118. if (_addMaterial != null)
  119. {
  120. Material.Destroy(_addMaterial);
  121. _addMaterial = null;
  122. }
  123. if (_divMaterial != null)
  124. {
  125. Material.Destroy(_divMaterial);
  126. _divMaterial = null;
  127. }
  128. if (_accum != null)
  129. {
  130. _accum.Release();
  131. RenderTexture.Destroy(_accum);
  132. _accum = null;
  133. }
  134. if (_lastComp != null)
  135. {
  136. _lastComp.Release();
  137. RenderTexture.Destroy(_lastComp);
  138. _lastComp = null;
  139. }
  140. }
  141. public void OnNumSamplesChanged()
  142. {
  143. if (_divMaterial != null)
  144. {
  145. _addMaterial.SetFloat(_propWeight, 1f);
  146. _divMaterial.SetFloat(_propNumSamples, _numSamples);
  147. }
  148. }
  149. private static float LerpUnclamped(float a, float b, float t)
  150. {
  151. return a + ((b - a) * t);
  152. }
  153. [SerializeField]
  154. public float _bias = 1f;
  155. private float _total = 0f;
  156. private void ApplyWeighting()
  157. {
  158. // Apply some frame weighting so the newer frames have the most contribution
  159. // Not sure this is better than non-weighted averaging.
  160. float weight = ((float)_frameCount / (float)_numSamples);
  161. weight = Mathf.Pow(weight, 2f);
  162. _total += weight;
  163. float numSamples = ((float)_numSamples / 2f) + 0.5f;
  164. numSamples = _total;
  165. weight = LerpUnclamped(weight, 1f, _bias);
  166. numSamples = LerpUnclamped(numSamples, _numSamples, _bias);
  167. _addMaterial.SetFloat(_propWeight, weight);
  168. _divMaterial.SetFloat(_propNumSamples, numSamples);
  169. }
  170. public void Accumulate(Texture src)
  171. {
  172. if (_isDirty)
  173. {
  174. Setup();
  175. }
  176. //ApplyWeighting();
  177. //_divMaterial.SetFloat(_propNumSamples, _numSamples);
  178. Graphics.Blit(src, _accum, _addMaterial);
  179. _frameCount++;
  180. if (_frameCount >= _numSamples)
  181. {
  182. // Divide the accumation texture
  183. Graphics.Blit(_accum, _lastComp, _divMaterial);
  184. // Clear the accumation texture so it is ready to start again
  185. ClearAccumulation();
  186. //Graphics.Blit(src, _accum, _addMaterial);
  187. //Graphics.Blit(_lastComp, _accum, _divMaterial);
  188. IsFrameAccumulated = true;
  189. _frameCount = 0;
  190. _total = 0f;
  191. }
  192. else
  193. {
  194. IsFrameAccumulated = false;
  195. }
  196. }
  197. private void OnRenderImage(RenderTexture src, RenderTexture dst)
  198. {
  199. // Take the result of the camera render and accumulate it
  200. // NOTE: Some capture components disable the this MotionBlur component so that OnRenderImage() isn't called as the component is used manually
  201. Accumulate(src);
  202. Graphics.Blit(_lastComp, dst);
  203. }
  204. /*void OnGUI()
  205. {
  206. GUILayout.Label("Real (slow) Motion Blur Demo");
  207. GUILayout.BeginHorizontal();
  208. GUILayout.Label("Samples");
  209. int numSamples = (int)GUILayout.HorizontalSlider(_numSamples, 1, 64, GUILayout.Width(128f));
  210. if (numSamples != _numSamples)
  211. {
  212. _numSamples = numSamples;
  213. OnNumSamplesChanged();
  214. }
  215. GUILayout.Label(_numSamples.ToString());
  216. GUILayout.EndHorizontal();
  217. }*/
  218. }
  219. }