CaptureFromCamera360ODS.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. #if UNITY_5_5_OR_NEWER
  2. #define AVPRO_MOVIECAPTURE_UNITYPROFILER_55
  3. #endif
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. //-----------------------------------------------------------------------------
  8. // Copyright 2012-2017 RenderHeads Ltd. All rights reserved.
  9. //-----------------------------------------------------------------------------
  10. namespace RenderHeads.Media.AVProMovieCapture
  11. {
  12. /// <summary>
  13. /// Capture to a stereo 360 view from camera in equi-rectangular format.
  14. /// Based on Google's Omni-direction Stereo paper: https://developers.google.com/vr/jump/rendering-ods-content.pdf
  15. /// </summary>
  16. [AddComponentMenu("AVPro Movie Capture/Capture From Camera 360 Stereo ODS (VR)", 101)]
  17. public class CaptureFromCamera360ODS : CaptureBase
  18. {
  19. [System.Serializable]
  20. public class Settings
  21. {
  22. [SerializeField]
  23. public Camera camera;
  24. [SerializeField]
  25. [Tooltip("Makes assumption that 1 Unity unit is 1m")]
  26. public float ipd = 0.064f;
  27. [SerializeField]
  28. [Tooltip("Higher value meant less slices to render, but can affect quality.")]
  29. public int pixelSliceSize = 1;
  30. [SerializeField]
  31. [Range(1, 31)]
  32. [Tooltip("May need to be increased to work with some post image effects. Value is in pixels.")]
  33. public int paddingSize = 1;
  34. [SerializeField]
  35. public CameraClearFlags cameraClearMode = CameraClearFlags.Color;
  36. [SerializeField]
  37. public Color cameraClearColor = Color.black;
  38. [SerializeField]
  39. public Behaviour[] cameraImageEffects;
  40. }
  41. [SerializeField]
  42. private Settings _settings = new Settings();
  43. public Settings Setup
  44. {
  45. get { return _settings; }
  46. }
  47. // State
  48. private int _eyeWidth = 1920;
  49. private int _eyeHeight = 1080;
  50. private Transform _cameraGroup;
  51. private Camera _leftCameraTop;
  52. private Camera _leftCameraBot;
  53. private Camera _rightCameraTop;
  54. private Camera _rightCameraBot;
  55. private RenderTexture _final;
  56. private System.IntPtr _targetNativePointer = System.IntPtr.Zero;
  57. private Material _finalMaterial;
  58. private int _propSliceCenter;
  59. public CaptureFromCamera360ODS()
  60. {
  61. // Override the default values to match more common use cases for this capture component
  62. _isRealTime = false;
  63. _renderResolution = Resolution.POW2_4096x4096;
  64. /*_settings.camera = this.GetComponent<Camera>();
  65. if (_settings.camera != null)
  66. {
  67. _settings.cameraClearMode = _settings.camera.clearFlags;
  68. _settings.cameraClearColor = _settings.camera.backgroundColor;
  69. }*/
  70. }
  71. public void SetCamera(Camera camera)
  72. {
  73. _settings.camera = camera;
  74. // TODO: add support for camera chains
  75. }
  76. public override void Start()
  77. {
  78. Shader mergeShader = Shader.Find("Hidden/AVProMovieCapture/ODSMerge");
  79. if (mergeShader != null)
  80. {
  81. _finalMaterial = new Material(mergeShader);
  82. }
  83. else
  84. {
  85. Debug.LogError("[AVProMovieCapture] Can't find Hidden/AVProMovieCapture/ODSMerge shader");
  86. }
  87. _propSliceCenter = Shader.PropertyToID("_sliceCenter");
  88. base.Start();
  89. }
  90. private Camera CreateEye(Camera camera, string gameObjectName, float yRot, float xOffset, int cameraTargetHeight, int cullingMask, float fov, float aspect, int aalevel)
  91. {
  92. bool isCreated = false;
  93. if (camera == null)
  94. {
  95. GameObject eye = new GameObject(gameObjectName);
  96. eye.transform.parent = _cameraGroup;
  97. eye.transform.rotation = Quaternion.AngleAxis(-yRot, Vector3.right);
  98. eye.transform.localPosition = new Vector3(xOffset, 0f, 0f);
  99. camera = eye.AddComponent<Camera>();
  100. isCreated = true;
  101. }
  102. camera.fieldOfView = fov;
  103. camera.aspect = aspect;
  104. camera.clearFlags = _settings.cameraClearMode;
  105. camera.backgroundColor = _settings.cameraClearColor;
  106. camera.cullingMask = cullingMask;
  107. camera.useOcclusionCulling = _settings.camera.useOcclusionCulling;
  108. camera.renderingPath = _settings.camera.renderingPath;
  109. #if UNITY_5_6_OR_NEWER
  110. camera.allowHDR = _settings.camera.allowHDR;
  111. camera.allowMSAA = _settings.camera.allowMSAA;
  112. if (camera.renderingPath == RenderingPath.DeferredShading
  113. #if AVPRO_MOVIECAPTURE_DEFERREDSHADING
  114. || camera.renderingPath == RenderingPath.DeferredLighting
  115. #endif
  116. )
  117. {
  118. camera.allowMSAA = false;
  119. }
  120. #endif
  121. {
  122. int textureWidth = _settings.pixelSliceSize + 2 * _settings.paddingSize;
  123. int textureHeight = cameraTargetHeight;
  124. if (camera.targetTexture != null)
  125. {
  126. camera.targetTexture.DiscardContents();
  127. if (camera.targetTexture.width != textureWidth || camera.targetTexture.height != textureHeight || camera.targetTexture.antiAliasing != aalevel)
  128. {
  129. RenderTexture.ReleaseTemporary(camera.targetTexture);
  130. camera.targetTexture = null;
  131. }
  132. }
  133. if (camera.targetTexture == null)
  134. {
  135. camera.targetTexture = RenderTexture.GetTemporary(textureWidth, textureHeight, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default, aalevel);
  136. }
  137. }
  138. camera.enabled = false;
  139. // TODO: make it so the components can be added/updated each time if they have changed
  140. if (isCreated)
  141. {
  142. if (_settings.cameraImageEffects != null)
  143. {
  144. for (int i = 0; i < _settings.cameraImageEffects.Length; i++)
  145. {
  146. Behaviour origComponent = _settings.cameraImageEffects[i];
  147. if (origComponent != null)
  148. {
  149. if (origComponent.enabled)
  150. {
  151. #if UNITY_EDITOR
  152. Component newComponent = camera.gameObject.AddComponent(origComponent.GetType());
  153. // TODO: we need to copy any post image effect component fields here via reflection? or perhaps use prefabs?
  154. UnityEditor.EditorUtility.CopySerialized(origComponent, newComponent);
  155. #endif
  156. }
  157. }
  158. else
  159. {
  160. Debug.LogWarning("[AVProMovieCapture] Image effect is null");
  161. }
  162. }
  163. }
  164. }
  165. return camera;
  166. }
  167. public override void UpdateFrame()
  168. {
  169. TickFrameTimer();
  170. if (_capturing && !_paused)
  171. {
  172. if (_settings.camera != null && _handle >= 0)
  173. {
  174. bool canGrab = true;
  175. if (IsUsingMotionBlur())
  176. {
  177. // If the motion blur is still accumulating, don't grab this frame
  178. canGrab = _motionBlur.IsFrameAccumulated;
  179. }
  180. if (canGrab && CanOutputFrame())
  181. {
  182. // Frame to encode either comes from rendering, or motion blur accumulation
  183. RenderTexture finalTexture = null;
  184. if (!IsUsingMotionBlur())
  185. {
  186. RenderFrame();
  187. finalTexture = _final;
  188. }
  189. else
  190. {
  191. finalTexture = _motionBlur.FinalTexture;
  192. }
  193. if (_targetNativePointer == System.IntPtr.Zero || _supportTextureRecreate)
  194. {
  195. // NOTE: If support for captures to survive through alt-tab events, or window resizes where the GPU resources are recreated
  196. // is required, then this line is needed. It is very expensive though as it does a sync with the rendering thread.
  197. _targetNativePointer = finalTexture.GetNativeTexturePtr();
  198. }
  199. NativePlugin.SetTexturePointer(_handle, _targetNativePointer);
  200. RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
  201. GL.InvalidateState();
  202. UpdateFPS();
  203. }
  204. }
  205. }
  206. base.UpdateFrame();
  207. RenormTimer();
  208. }
  209. private void LateUpdate()
  210. {
  211. AccumulateMotionBlur();
  212. }
  213. private void AccumulateMotionBlur()
  214. {
  215. if (_motionBlur != null)
  216. {
  217. if (_capturing && !_paused)
  218. {
  219. if (_settings.camera != null && _handle >= 0)
  220. {
  221. RenderFrame();
  222. _motionBlur.Accumulate(_final);
  223. }
  224. }
  225. }
  226. }
  227. private void RenderFrame()
  228. {
  229. _cameraGroup.position = _settings.camera.transform.position;
  230. Quaternion originalRot = _settings.camera.transform.rotation * Quaternion.AngleAxis(180f, Vector3.up); // Apply 180 degree correction align the front camera vector to the center of the output image
  231. float rotationStep = 360f / _eyeWidth;
  232. for (int i = 0; i < _eyeWidth / _settings.pixelSliceSize; ++i)
  233. {
  234. int step = i * _settings.pixelSliceSize;
  235. float v = step * rotationStep;
  236. _cameraGroup.rotation = originalRot * Quaternion.AngleAxis(v, Vector3.up);
  237. _leftCameraTop.targetTexture.DiscardContents();
  238. _leftCameraBot.targetTexture.DiscardContents();
  239. _rightCameraTop.targetTexture.DiscardContents();
  240. _rightCameraBot.targetTexture.DiscardContents();
  241. _leftCameraTop.Render();
  242. _leftCameraBot.Render();
  243. _rightCameraTop.Render();
  244. _rightCameraBot.Render();
  245. _finalMaterial.SetFloat(_propSliceCenter, step + _settings.pixelSliceSize / 2f);
  246. _final.DiscardContents();
  247. Graphics.Blit(null, _final, _finalMaterial);
  248. //System.GC.Collect();
  249. }
  250. }
  251. public override Texture GetPreviewTexture()
  252. {
  253. if (IsUsingMotionBlur())
  254. {
  255. return _motionBlur.FinalTexture;
  256. }
  257. return _final;
  258. }
  259. public override bool PrepareCapture()
  260. {
  261. if (_capturing)
  262. {
  263. return false;
  264. }
  265. if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("emulated"))
  266. {
  267. Debug.LogError("[AVProMovieCapture] OpenGL not yet supported for CaptureFromCamera360ODS component, please use Direct3D11 instead. You may need to switch your build platform to Windows.");
  268. return false;
  269. }
  270. #if AVPRO_MOVIECAPTURE_UNITYPROFILER_55
  271. // This component makes the profiler use a TON of memory, so warn the user to disable it
  272. if (UnityEngine.Profiling.Profiler.enabled)
  273. {
  274. Debug.LogWarning("[AVProMovieCapture] Having the Unity profiler enabled while using the CaptureFromCamera360ODS component is not recommended. Too many samples are generated which can make the system run out of memory. Disable the profiler, close the window and remove the tab. A Unity restart may be required after disabling the profiler recording");
  275. }
  276. #endif
  277. // Setup material
  278. _pixelFormat = NativePlugin.PixelFormat.RGBA32;
  279. _isTopDown = true;
  280. if (_settings.camera == null)
  281. {
  282. _settings.camera = this.GetComponent<Camera>();
  283. }
  284. if (_settings.camera == null)
  285. {
  286. Debug.LogError("[AVProMovieCapture] No camera assigned to CaptureFromCamera360ODS");
  287. return false;
  288. }
  289. // Resolution
  290. int finalWidth = Mathf.FloorToInt(_settings.camera.pixelRect.width);
  291. int finalHeight = Mathf.FloorToInt(_settings.camera.pixelRect.height);
  292. if (_renderResolution == Resolution.Custom)
  293. {
  294. finalWidth = (int)_renderSize.x;
  295. finalHeight = (int)_renderSize.y;
  296. }
  297. else if (_renderResolution != Resolution.Original)
  298. {
  299. GetResolution(_renderResolution, ref finalWidth, ref finalHeight);
  300. }
  301. _eyeWidth = Mathf.Clamp(finalWidth, 1, 8192);
  302. // NOTE: Height must be even
  303. _eyeHeight = Mathf.Clamp(finalHeight / 2, 1, 4096);
  304. _eyeHeight -= _eyeHeight & 1;
  305. finalWidth = _eyeWidth;
  306. finalHeight = _eyeHeight * 2;
  307. int aaLevel = GetCameraAntiAliasingLevel(_settings.camera);
  308. // NOTE: Pixel slice size must be divisible by the total width
  309. _settings.pixelSliceSize = Mathf.Clamp(_settings.pixelSliceSize, 1, _eyeWidth);
  310. _settings.pixelSliceSize = _settings.pixelSliceSize - (_eyeWidth % _settings.pixelSliceSize);
  311. _settings.paddingSize = Mathf.Clamp(_settings.paddingSize, 0, 31);
  312. float offset = _settings.ipd / 2f;
  313. float aspect = (_settings.pixelSliceSize * 2f) / _eyeHeight;
  314. if (_cameraGroup == null)
  315. {
  316. GameObject go = new GameObject("OdsCameraGroup");
  317. go.transform.parent = this.gameObject.transform;
  318. _cameraGroup = go.transform;
  319. }
  320. // TODO: only recreate textures if they don't already exist or size has changed
  321. _leftCameraTop = CreateEye(_leftCameraTop, "LeftEyeTop", 45f, -offset, _eyeHeight / 2, _settings.camera.cullingMask, 90f, aspect, aaLevel);
  322. _leftCameraBot = CreateEye(_leftCameraBot, "LeftEyeBot", -45f, -offset, _eyeHeight / 2, _settings.camera.cullingMask, 90f, aspect, aaLevel);
  323. _rightCameraTop = CreateEye(_rightCameraTop, "RightEyeTop", 45f, offset, _eyeHeight / 2, _settings.camera.cullingMask, 90f, aspect, aaLevel);
  324. _rightCameraBot = CreateEye(_rightCameraBot, "RightEyeBot", -45f, offset, _eyeHeight / 2, _settings.camera.cullingMask, 90f, aspect, aaLevel);
  325. // Create final texture (if not already created)
  326. _targetNativePointer = System.IntPtr.Zero;
  327. if (_final != null)
  328. {
  329. _final.DiscardContents();
  330. if (_final.width != finalWidth || _final.height != finalHeight || _final.antiAliasing != aaLevel)
  331. {
  332. RenderTexture.ReleaseTemporary(_final);
  333. _final = null;
  334. }
  335. _final = null;
  336. }
  337. if (_final == null)
  338. {
  339. _final = RenderTexture.GetTemporary(finalWidth, finalHeight, 0);
  340. }
  341. // Setup material
  342. _finalMaterial.SetTexture("_leftTopTex", _leftCameraTop.targetTexture);
  343. _finalMaterial.SetTexture("_leftBotTex", _leftCameraBot.targetTexture);
  344. _finalMaterial.SetTexture("_rightTopTex", _rightCameraTop.targetTexture);
  345. _finalMaterial.SetTexture("_rightBotTex", _rightCameraBot.targetTexture);
  346. _finalMaterial.SetFloat("_pixelSliceSize", _settings.pixelSliceSize);
  347. _finalMaterial.SetInt("_paddingSize", _settings.paddingSize);
  348. _finalMaterial.SetFloat("_targetXTexelSize", 1.0f / finalWidth);
  349. // Setup capture
  350. SelectRecordingResolution(finalWidth, finalHeight);
  351. GenerateFilename();
  352. return base.PrepareCapture();
  353. }
  354. public override void OnDestroy()
  355. {
  356. _targetNativePointer = System.IntPtr.Zero;
  357. if (_final != null)
  358. {
  359. RenderTexture.ReleaseTemporary(_final);
  360. _final = null;
  361. }
  362. if (_leftCameraTop != null)
  363. {
  364. RenderTexture.ReleaseTemporary(_leftCameraTop.targetTexture);
  365. GameObject.Destroy(_leftCameraTop.gameObject);
  366. _leftCameraTop = null;
  367. }
  368. if (_leftCameraBot != null)
  369. {
  370. RenderTexture.ReleaseTemporary(_leftCameraBot.targetTexture);
  371. GameObject.Destroy(_leftCameraBot.gameObject);
  372. _leftCameraBot = null;
  373. }
  374. if (_rightCameraTop != null)
  375. {
  376. RenderTexture.ReleaseTemporary(_rightCameraTop.targetTexture);
  377. GameObject.Destroy(_rightCameraTop.gameObject);
  378. _rightCameraTop = null;
  379. }
  380. if (_rightCameraBot != null)
  381. {
  382. RenderTexture.ReleaseTemporary(_rightCameraBot.targetTexture);
  383. GameObject.Destroy(_rightCameraBot.gameObject);
  384. _rightCameraBot = null;
  385. }
  386. if (_cameraGroup != null)
  387. {
  388. GameObject.Destroy(_cameraGroup.gameObject);
  389. _cameraGroup = null;
  390. }
  391. if (_finalMaterial)
  392. {
  393. Destroy(_finalMaterial);
  394. _finalMaterial = null;
  395. }
  396. base.OnDestroy();
  397. }
  398. }
  399. }