UnityMol  0.9.6-875
UnityMol viewer / In developement
CameraMotionBlur.cs
Go to the documentation of this file.
1 using System;
2 using UnityEngine;
3 
4 namespace UnityStandardAssets.ImageEffects
5 {
6  [ExecuteInEditMode]
7  [RequireComponent (typeof(Camera))]
8  [AddComponentMenu ("Image Effects/Camera/Camera Motion Blur") ]
10  {
11  // make sure to match this to MAX_RADIUS in shader ('k' in paper)
12  static float MAX_RADIUS = 10.0f;
13 
14  public enum MotionBlurFilter {
15  CameraMotion = 0, // global screen blur based on cam motion
16  LocalBlur = 1, // cheap blur, no dilation or scattering
17  Reconstruction = 2, // advanced filter (simulates scattering) as in plausible motion blur paper
18  ReconstructionDX11 = 3, // advanced filter (simulates scattering) as in plausible motion blur paper
19  ReconstructionDisc = 4, // advanced filter using scaled poisson disc sampling
20  }
21 
22  // settings
23  public MotionBlurFilter filterType = MotionBlurFilter.Reconstruction;
24  public bool preview = false; // show how blur would look like in action ...
25  public Vector3 previewScale = Vector3.one; // ... given this movement vector
26 
27  // params
28  public float movementScale = 0.0f;
29  public float rotationScale = 1.0f;
30  public float maxVelocity = 8.0f; // maximum velocity in pixels
31  public float minVelocity = 0.1f; // minimum velocity in pixels
32  public float velocityScale = 0.375f; // global velocity scale
33  public float softZDistance = 0.005f; // for z overlap check softness (reconstruction filter only)
34  public int velocityDownsample = 1; // low resolution velocity buffer? (optimization)
35  public LayerMask excludeLayers = 0;
36  private GameObject tmpCam = null;
37 
38  // resources
39  public Shader shader;
40  public Shader dx11MotionBlurShader;
41  public Shader replacementClear;
42 
43  private Material motionBlurMaterial = null;
44  private Material dx11MotionBlurMaterial = null;
45 
46  public Texture2D noiseTexture = null;
47  public float jitter = 0.05f;
48 
49  // (internal) debug
50  public bool showVelocity = false;
51  public float showVelocityScale = 1.0f;
52 
53  // camera transforms
54  private Matrix4x4 currentViewProjMat;
55  private Matrix4x4 prevViewProjMat;
56  private int prevFrameCount;
57  private bool wasActive;
58  // shortcuts to calculate global blur direction when using 'CameraMotion'
59  private Vector3 prevFrameForward = Vector3.forward;
60  private Vector3 prevFrameUp = Vector3.up;
61  private Vector3 prevFramePos = Vector3.zero;
62  private Camera _camera;
63 
64 
65  private void CalculateViewProjection () {
66  Matrix4x4 viewMat = _camera.worldToCameraMatrix;
67  Matrix4x4 projMat = GL.GetGPUProjectionMatrix (_camera.projectionMatrix, true);
68  currentViewProjMat = projMat * viewMat;
69  }
70 
71 
72  new void Start () {
73  CheckResources ();
74 
75  if (_camera == null)
76  _camera = GetComponent<Camera>();
77 
78  wasActive = gameObject.activeInHierarchy;
79  CalculateViewProjection ();
80  Remember ();
81  wasActive = false; // hack to fake position/rotation update and prevent bad blurs
82  }
83 
84  void OnEnable () {
85 
86  if (_camera == null)
87  _camera = GetComponent<Camera>();
88 
89  _camera.depthTextureMode |= DepthTextureMode.Depth;
90  }
91 
92  void OnDisable () {
93  if (null != motionBlurMaterial) {
94  DestroyImmediate (motionBlurMaterial);
95  motionBlurMaterial = null;
96  }
97  if (null != dx11MotionBlurMaterial) {
98  DestroyImmediate (dx11MotionBlurMaterial);
99  dx11MotionBlurMaterial = null;
100  }
101  if (null != tmpCam) {
102  DestroyImmediate (tmpCam);
103  tmpCam = null;
104  }
105  }
106 
107 
108  public override bool CheckResources () {
109  CheckSupport (true, true); // depth & hdr needed
110  motionBlurMaterial = CheckShaderAndCreateMaterial (shader, motionBlurMaterial);
111 
112  if (supportDX11 && filterType == MotionBlurFilter.ReconstructionDX11) {
113  dx11MotionBlurMaterial = CheckShaderAndCreateMaterial (dx11MotionBlurShader, dx11MotionBlurMaterial);
114  }
115 
116  if (!isSupported)
117  ReportAutoDisable ();
118 
119  return isSupported;
120  }
121 
122  void OnRenderImage (RenderTexture source, RenderTexture destination) {
123  if (false == CheckResources ()) {
124  Graphics.Blit (source, destination);
125  return;
126  }
127 
128  if (filterType == MotionBlurFilter.CameraMotion)
129  StartFrame ();
130 
131  // use if possible new RG format ... fallback to half otherwise
132  var rtFormat= SystemInfo.SupportsRenderTextureFormat (RenderTextureFormat.RGHalf) ? RenderTextureFormat.RGHalf : RenderTextureFormat.ARGBHalf;
133 
134  // get temp textures
135  RenderTexture velBuffer = RenderTexture.GetTemporary (divRoundUp (source.width, velocityDownsample), divRoundUp (source.height, velocityDownsample), 0, rtFormat);
136  int tileWidth = 1;
137  int tileHeight = 1;
138  maxVelocity = Mathf.Max (2.0f, maxVelocity);
139 
140  float _maxVelocity = maxVelocity; // calculate 'k'
141  // note: 's' is hardcoded in shaders except for DX11 path
142 
143  // auto DX11 fallback!
144  bool fallbackFromDX11 = filterType == MotionBlurFilter.ReconstructionDX11 && dx11MotionBlurMaterial == null;
145 
146  if (filterType == MotionBlurFilter.Reconstruction || fallbackFromDX11 || filterType == MotionBlurFilter.ReconstructionDisc) {
147  maxVelocity = Mathf.Min (maxVelocity, MAX_RADIUS);
148  tileWidth = divRoundUp (velBuffer.width, (int) maxVelocity);
149  tileHeight = divRoundUp (velBuffer.height, (int) maxVelocity);
150  _maxVelocity = velBuffer.width/tileWidth;
151  }
152  else {
153  tileWidth = divRoundUp (velBuffer.width, (int) maxVelocity);
154  tileHeight = divRoundUp (velBuffer.height, (int) maxVelocity);
155  _maxVelocity = velBuffer.width/tileWidth;
156  }
157 
158  RenderTexture tileMax = RenderTexture.GetTemporary (tileWidth, tileHeight, 0, rtFormat);
159  RenderTexture neighbourMax = RenderTexture.GetTemporary (tileWidth, tileHeight, 0, rtFormat);
160  velBuffer.filterMode = FilterMode.Point;
161  tileMax.filterMode = FilterMode.Point;
162  neighbourMax.filterMode = FilterMode.Point;
163  if (noiseTexture) noiseTexture.filterMode = FilterMode.Point;
164  source.wrapMode = TextureWrapMode.Clamp;
165  velBuffer.wrapMode = TextureWrapMode.Clamp;
166  neighbourMax.wrapMode = TextureWrapMode.Clamp;
167  tileMax.wrapMode = TextureWrapMode.Clamp;
168 
169  // calc correct viewprj matrix
170  CalculateViewProjection ();
171 
172  // just started up?
173  if (gameObject.activeInHierarchy && !wasActive) {
174  Remember ();
175  }
176  wasActive = gameObject.activeInHierarchy;
177 
178  // matrices
179  Matrix4x4 invViewPrj = Matrix4x4.Inverse (currentViewProjMat);
180  motionBlurMaterial.SetMatrix ("_InvViewProj", invViewPrj);
181  motionBlurMaterial.SetMatrix ("_PrevViewProj", prevViewProjMat);
182  motionBlurMaterial.SetMatrix ("_ToPrevViewProjCombined", prevViewProjMat * invViewPrj);
183 
184  motionBlurMaterial.SetFloat ("_MaxVelocity", _maxVelocity);
185  motionBlurMaterial.SetFloat ("_MaxRadiusOrKInPaper", _maxVelocity);
186  motionBlurMaterial.SetFloat ("_MinVelocity", minVelocity);
187  motionBlurMaterial.SetFloat ("_VelocityScale", velocityScale);
188  motionBlurMaterial.SetFloat ("_Jitter", jitter);
189 
190  // texture samplers
191  motionBlurMaterial.SetTexture ("_NoiseTex", noiseTexture);
192  motionBlurMaterial.SetTexture ("_VelTex", velBuffer);
193  motionBlurMaterial.SetTexture ("_NeighbourMaxTex", neighbourMax);
194  motionBlurMaterial.SetTexture ("_TileTexDebug", tileMax);
195 
196  if (preview) {
197  // generate an artifical 'previous' matrix to simulate blur look
198  Matrix4x4 viewMat = _camera.worldToCameraMatrix;
199  Matrix4x4 offset = Matrix4x4.identity;
200  offset.SetTRS(previewScale * 0.3333f, Quaternion.identity, Vector3.one); // using only translation
201  Matrix4x4 projMat = GL.GetGPUProjectionMatrix (_camera.projectionMatrix, true);
202  prevViewProjMat = projMat * offset * viewMat;
203  motionBlurMaterial.SetMatrix ("_PrevViewProj", prevViewProjMat);
204  motionBlurMaterial.SetMatrix ("_ToPrevViewProjCombined", prevViewProjMat * invViewPrj);
205  }
206 
207  if (filterType == MotionBlurFilter.CameraMotion)
208  {
209  // build blur vector to be used in shader to create a global blur direction
210  Vector4 blurVector = Vector4.zero;
211 
212  float lookUpDown = Vector3.Dot (transform.up, Vector3.up);
213  Vector3 distanceVector = prevFramePos-transform.position;
214 
215  float distMag = distanceVector.magnitude;
216 
217  float farHeur = 1.0f;
218 
219  // pitch (vertical)
220  farHeur = (Vector3.Angle (transform.up, prevFrameUp) / _camera.fieldOfView) * (source.width * 0.75f);
221  blurVector.x = rotationScale * farHeur;//Mathf.Clamp01((1.0ff-Vector3.Dot(transform.up, prevFrameUp)));
222 
223  // yaw #1 (horizontal, faded by pitch)
224  farHeur = (Vector3.Angle (transform.forward, prevFrameForward) / _camera.fieldOfView) * (source.width * 0.75f);
225  blurVector.y = rotationScale * lookUpDown * farHeur;//Mathf.Clamp01((1.0ff-Vector3.Dot(transform.forward, prevFrameForward)));
226 
227  // yaw #2 (when looking down, faded by 1-pitch)
228  farHeur = (Vector3.Angle (transform.forward, prevFrameForward) / _camera.fieldOfView) * (source.width * 0.75f);
229  blurVector.z = rotationScale * (1.0f- lookUpDown) * farHeur;//Mathf.Clamp01((1.0ff-Vector3.Dot(transform.forward, prevFrameForward)));
230 
231  if (distMag > Mathf.Epsilon && movementScale > Mathf.Epsilon) {
232  // forward (probably most important)
233  blurVector.w = movementScale * (Vector3.Dot (transform.forward, distanceVector) ) * (source.width * 0.5f);
234  // jump (maybe scale down further)
235  blurVector.x += movementScale * (Vector3.Dot (transform.up, distanceVector) ) * (source.width * 0.5f);
236  // strafe (maybe scale down further)
237  blurVector.y += movementScale * (Vector3.Dot (transform.right, distanceVector) ) * (source.width * 0.5f);
238  }
239 
240  if (preview) // crude approximation
241  motionBlurMaterial.SetVector ("_BlurDirectionPacked", new Vector4 (previewScale.y, previewScale.x, 0.0f, previewScale.z) * 0.5f * _camera.fieldOfView);
242  else
243  motionBlurMaterial.SetVector ("_BlurDirectionPacked", blurVector);
244  }
245  else {
246  // generate velocity buffer
247  Graphics.Blit (source, velBuffer, motionBlurMaterial, 0);
248 
249  // patch up velocity buffer:
250 
251  // exclude certain layers (e.g. skinned objects as we cant really support that atm)
252 
253  Camera cam = null;
254  if (excludeLayers.value != 0)// || dynamicLayers.value)
255  cam = GetTmpCam ();
256 
257  if (cam && excludeLayers.value != 0 && replacementClear && replacementClear.isSupported) {
258  cam.targetTexture = velBuffer;
259  cam.cullingMask = excludeLayers;
260  cam.RenderWithShader (replacementClear, "");
261  }
262  }
263 
264  if (!preview && Time.frameCount != prevFrameCount) {
265  // remember current transformation data for next frame
266  prevFrameCount = Time.frameCount;
267  Remember ();
268  }
269 
270  source.filterMode = FilterMode.Bilinear;
271 
272  // debug vel buffer:
273  if (showVelocity) {
274  // generate tile max and neighbour max
275  //Graphics.Blit (velBuffer, tileMax, motionBlurMaterial, 2);
276  //Graphics.Blit (tileMax, neighbourMax, motionBlurMaterial, 3);
277  motionBlurMaterial.SetFloat ("_DisplayVelocityScale", showVelocityScale);
278  Graphics.Blit (velBuffer, destination, motionBlurMaterial, 1);
279  }
280  else {
281  if (filterType == MotionBlurFilter.ReconstructionDX11 && !fallbackFromDX11) {
282  // need to reset some parameters for dx11 shader
283  dx11MotionBlurMaterial.SetFloat ("_MinVelocity", minVelocity);
284  dx11MotionBlurMaterial.SetFloat ("_VelocityScale", velocityScale);
285  dx11MotionBlurMaterial.SetFloat ("_Jitter", jitter);
286 
287  // texture samplers
288  dx11MotionBlurMaterial.SetTexture ("_NoiseTex", noiseTexture);
289  dx11MotionBlurMaterial.SetTexture ("_VelTex", velBuffer);
290  dx11MotionBlurMaterial.SetTexture ("_NeighbourMaxTex", neighbourMax);
291 
292  dx11MotionBlurMaterial.SetFloat ("_SoftZDistance", Mathf.Max(0.00025f, softZDistance) );
293  dx11MotionBlurMaterial.SetFloat ("_MaxRadiusOrKInPaper", _maxVelocity);
294 
295  // generate tile max and neighbour max
296  Graphics.Blit (velBuffer, tileMax, dx11MotionBlurMaterial, 0);
297  Graphics.Blit (tileMax, neighbourMax, dx11MotionBlurMaterial, 1);
298 
299  // final blur
300  Graphics.Blit (source, destination, dx11MotionBlurMaterial, 2);
301  }
302  else if (filterType == MotionBlurFilter.Reconstruction || fallbackFromDX11) {
303  // 'reconstructing' properly integrated color
304  motionBlurMaterial.SetFloat ("_SoftZDistance", Mathf.Max(0.00025f, softZDistance) );
305 
306  // generate tile max and neighbour max
307  Graphics.Blit (velBuffer, tileMax, motionBlurMaterial, 2);
308  Graphics.Blit (tileMax, neighbourMax, motionBlurMaterial, 3);
309 
310  // final blur
311  Graphics.Blit (source, destination, motionBlurMaterial, 4);
312  }
313  else if (filterType == MotionBlurFilter.CameraMotion) {
314  // orange box style motion blur
315  Graphics.Blit (source, destination, motionBlurMaterial, 6);
316  }
317  else if (filterType == MotionBlurFilter.ReconstructionDisc) {
318  // dof style motion blur defocuing and ellipse around the princical blur direction
319  // 'reconstructing' properly integrated color
320  motionBlurMaterial.SetFloat ("_SoftZDistance", Mathf.Max(0.00025f, softZDistance) );
321 
322  // generate tile max and neighbour max
323  Graphics.Blit (velBuffer, tileMax, motionBlurMaterial, 2);
324  Graphics.Blit (tileMax, neighbourMax, motionBlurMaterial, 3);
325 
326  Graphics.Blit (source, destination, motionBlurMaterial, 7);
327  }
328  else {
329  // simple & fast blur (low quality): just blurring along velocity
330  Graphics.Blit (source, destination, motionBlurMaterial, 5);
331  }
332  }
333 
334  // cleanup
335  RenderTexture.ReleaseTemporary (velBuffer);
336  RenderTexture.ReleaseTemporary (tileMax);
337  RenderTexture.ReleaseTemporary (neighbourMax);
338  }
339 
340  void Remember () {
341  prevViewProjMat = currentViewProjMat;
342  prevFrameForward = transform.forward;
343  prevFrameUp = transform.up;
344  prevFramePos = transform.position;
345  }
346 
347  Camera GetTmpCam () {
348  if (tmpCam == null) {
349  string name = "_" + _camera.name + "_MotionBlurTmpCam";
350  GameObject go = GameObject.Find (name);
351  if (null == go) // couldn't find, recreate
352  tmpCam = new GameObject (name, typeof (Camera));
353  else
354  tmpCam = go;
355  }
356 
357  tmpCam.hideFlags = HideFlags.DontSave;
358  tmpCam.transform.position = _camera.transform.position;
359  tmpCam.transform.rotation = _camera.transform.rotation;
360  tmpCam.transform.localScale = _camera.transform.localScale;
361  tmpCam.GetComponent<Camera>().CopyFrom(_camera);
362 
363  tmpCam.GetComponent<Camera>().enabled = false;
364  tmpCam.GetComponent<Camera>().depthTextureMode = DepthTextureMode.None;
365  tmpCam.GetComponent<Camera>().clearFlags = CameraClearFlags.Nothing;
366 
367  return tmpCam.GetComponent<Camera>();
368  }
369 
370  void StartFrame () {
371  // take only x% of positional changes into account (camera motion)
372  // TODO: possibly do the same for rotational part
373  prevFramePos = Vector3.Slerp(prevFramePos, transform.position, 0.75f);
374  }
375 
376  static int divRoundUp (int x, int d)
377  {
378  return (x + d - 1) / d;
379  }
380  }
381 }
void OnRenderImage(RenderTexture source, RenderTexture destination)