CaptureFromCamera360.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. #if UNITY_5_4_OR_NEWER || (UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3)
  2. #define AVPRO_MOVIECAPTURE_RENDERTEXTUREDIMENSIONS_54
  3. #endif
  4. #if UNITY_5_6_0 || UNITY_5_6_1
  5. #define AVPRO_MOVIECAPTURE_UNITYBUG_RENDERTOCUBEMAP_56
  6. #endif
  7. using UnityEngine;
  8. //-----------------------------------------------------------------------------
  9. // Copyright 2012-2017 RenderHeads Ltd. All rights reserved.
  10. //-----------------------------------------------------------------------------
  11. namespace RenderHeads.Media.AVProMovieCapture
  12. {
  13. /// <summary>
  14. /// Capture a camera view in 360 equi-rectangular format.
  15. /// The camera is rendered into a cubemap, so the scene is rendered an extra 6 times.
  16. /// Finally the cubemap is converted to equi-rectangular format and encoded.
  17. /// </summary>
  18. //[RequireComponent(typeof(Camera))]
  19. [AddComponentMenu("AVPro Movie Capture/Capture From Camera 360 (VR)", 100)]
  20. public class CaptureFromCamera360 : CaptureBase
  21. {
  22. // Cube map options
  23. [SerializeField]
  24. public int _cubemapResolution = 2048;
  25. [SerializeField]
  26. public int _cubemapDepth = 24;
  27. [SerializeField]
  28. public bool _supportGUI = true;
  29. [SerializeField]
  30. public bool _supportCameraRotation = true;
  31. [SerializeField]
  32. public StereoPacking _stereoRendering = StereoPacking.None;
  33. [SerializeField]
  34. [Tooltip("Makes assumption that 1 Unity unit is 1m")]
  35. public float _ipd = 0.064f;
  36. [SerializeField]
  37. private Camera _camera;
  38. // State
  39. private RenderTexture _faceTarget;
  40. private Material _blitMaterial;
  41. private Material _cubemapToEquirectangularMaterial;
  42. private RenderTexture _cubeTarget;
  43. private RenderTexture _finalTarget;
  44. private System.IntPtr _targetNativePointer = System.IntPtr.Zero;
  45. private int _propFlipX;
  46. public CaptureFromCamera360()
  47. {
  48. // Override the default values to match more common use cases for this capture component
  49. _renderResolution = Resolution.POW2_2048x2048;
  50. }
  51. protected bool IsManualCubemapRendering()
  52. {
  53. return (_supportGUI || _supportCameraRotation || _stereoRendering != StereoPacking.None);
  54. }
  55. public void SetCamera(Camera camera)
  56. {
  57. _camera = camera;
  58. }
  59. #if false
  60. private void OnRenderImage(RenderTexture source, RenderTexture dest)
  61. {
  62. #if false
  63. if (_capturing && !_paused)
  64. {
  65. while (_handle >= 0 && !NativePlugin.IsNewFrameDue(_handle))
  66. {
  67. System.Threading.Thread.Sleep(1);
  68. }
  69. if (_handle >= 0)
  70. {
  71. if (_audioCapture && _audioDeviceIndex < 0 && !_noAudio)
  72. {
  73. uint bufferLength = (uint)_audioCapture.BufferLength;
  74. if (bufferLength > 0)
  75. {
  76. NativePlugin.EncodeAudio(_handle, _audioCapture.BufferPtr, bufferLength);
  77. _audioCapture.FlushBuffer();
  78. }
  79. }
  80. // In Direct3D the RT can be flipped vertically
  81. /*if (source.texelSize.y < 0)
  82. {
  83. }*/
  84. Graphics.Blit(_cubeTarget, _target, _cubemapToEquirectangularMaterial);
  85. RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
  86. GL.InvalidateState();
  87. UpdateFPS();
  88. }
  89. }
  90. #endif
  91. // Pass-through
  92. if (_cubeTarget != null)
  93. {
  94. Graphics.Blit(_cubeTarget, dest, _cubemapToEquirectangularMaterial);
  95. }
  96. else
  97. {
  98. Graphics.Blit(source, dest);
  99. }
  100. }
  101. #endif
  102. public override void UpdateFrame()
  103. {
  104. TickFrameTimer();
  105. if (_capturing && !_paused)
  106. {
  107. if (_cubeTarget != null && _camera != null)
  108. {
  109. bool canGrab = true;
  110. if (IsUsingMotionBlur())
  111. {
  112. // TODO: fix motion blur
  113. //this._motionBlur.RenderImage()
  114. // If the motion blur is still accumulating, don't grab this frame
  115. canGrab = _motionBlur.IsFrameAccumulated;
  116. }
  117. if (canGrab && CanOutputFrame())
  118. {
  119. if (IsRecordingUnityAudio())
  120. {
  121. int audioDataLength = 0;
  122. System.IntPtr audioDataPtr = _audioCapture.ReadData(out audioDataLength);
  123. if (audioDataLength > 0)
  124. {
  125. NativePlugin.EncodeAudio(_handle, audioDataPtr, (uint)audioDataLength);
  126. }
  127. }
  128. RenderTexture finalTexture = _finalTarget;
  129. if (!IsUsingMotionBlur())
  130. {
  131. UpdateTexture();
  132. }
  133. else
  134. {
  135. finalTexture = _motionBlur.FinalTexture;
  136. }
  137. if (_targetNativePointer == System.IntPtr.Zero || _supportTextureRecreate)
  138. {
  139. // NOTE: If support for captures to survive through alt-tab events, or window resizes where the GPU resources are recreated
  140. // is required, then this line is needed. It is very expensive though as it does a sync with the rendering thread.
  141. _targetNativePointer = finalTexture.GetNativeTexturePtr();
  142. }
  143. NativePlugin.SetTexturePointer(_handle, _targetNativePointer);
  144. RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
  145. GL.InvalidateState();
  146. UpdateFPS();
  147. }
  148. }
  149. }
  150. base.UpdateFrame();
  151. RenormTimer();
  152. }
  153. private static void ClearCubemap(RenderTexture texture, Color color)
  154. {
  155. // TODO: Find a better way to do this?
  156. bool clearDepth = (texture.depth != 0);
  157. Graphics.SetRenderTarget(texture, 0, CubemapFace.PositiveX);
  158. GL.Clear(true, clearDepth, color);
  159. Graphics.SetRenderTarget(texture, 0, CubemapFace.PositiveY);
  160. GL.Clear(true, clearDepth, color);
  161. Graphics.SetRenderTarget(texture, 0, CubemapFace.PositiveZ);
  162. GL.Clear(true, clearDepth, color);
  163. Graphics.SetRenderTarget(texture, 0, CubemapFace.NegativeX);
  164. GL.Clear(true, clearDepth, color);
  165. Graphics.SetRenderTarget(texture, 0, CubemapFace.NegativeY);
  166. GL.Clear(true, clearDepth, color);
  167. Graphics.SetRenderTarget(texture, 0, CubemapFace.NegativeZ);
  168. GL.Clear(true, clearDepth, color);
  169. Graphics.SetRenderTarget(null);
  170. }
  171. private void UpdateTexture()
  172. {
  173. // In Direct3D the RT can be flipped vertically
  174. /*if (source.texelSize.y < 0)
  175. {
  176. }*/
  177. //_cubeCamera.transform.position = _camera.transform.position;
  178. //_cubeCamera.transform.rotation = _camera.transform.rotation;
  179. Camera camera = _camera;
  180. _cubeTarget.DiscardContents();
  181. if (_stereoRendering == StereoPacking.None)
  182. {
  183. if (!IsManualCubemapRendering())
  184. {
  185. #if AVPRO_MOVIECAPTURE_UNITYBUG_RENDERTOCUBEMAP_56
  186. RenderTexture prev = camera.targetTexture;
  187. #endif
  188. // Note: Camera.RenderToCubemap() doesn't support camera rotation
  189. camera.RenderToCubemap(_cubeTarget, 63);
  190. #if AVPRO_MOVIECAPTURE_UNITYBUG_RENDERTOCUBEMAP_56
  191. // NOTE: We need this to clean up the state in at least Unity 5.6.0 - 5.6.1p1
  192. camera.targetTexture = prev;
  193. #endif
  194. }
  195. else
  196. {
  197. RenderCameraToCubemap(camera, _cubeTarget);
  198. }
  199. _finalTarget.DiscardContents();
  200. Graphics.Blit(_cubeTarget, _finalTarget, _cubemapToEquirectangularMaterial);
  201. }
  202. else
  203. {
  204. // Save camera state
  205. Vector3 cameraPosition = camera.transform.localPosition;
  206. //Left eye
  207. camera.transform.Translate(new Vector3(-_ipd / 2f, 0f, 0f), Space.Self);
  208. RenderCameraToCubemap(camera, _cubeTarget);
  209. if (_stereoRendering == StereoPacking.TopBottom)
  210. {
  211. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_BOTTOM");
  212. _cubemapToEquirectangularMaterial.EnableKeyword("STEREOPACK_TOP");
  213. }
  214. else if (_stereoRendering == StereoPacking.LeftRight)
  215. {
  216. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_RIGHT");
  217. _cubemapToEquirectangularMaterial.EnableKeyword("STEREOPACK_LEFT");
  218. }
  219. _finalTarget.DiscardContents();
  220. Graphics.Blit(_cubeTarget, _finalTarget, _cubemapToEquirectangularMaterial);
  221. // Right eye
  222. camera.transform.localPosition = cameraPosition;
  223. camera.transform.Translate(new Vector3(_ipd / 2f, 0f, 0f), Space.Self);
  224. _cubeTarget.DiscardContents();
  225. RenderCameraToCubemap(camera, _cubeTarget);
  226. if (_stereoRendering == StereoPacking.TopBottom)
  227. {
  228. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_TOP");
  229. _cubemapToEquirectangularMaterial.EnableKeyword("STEREOPACK_BOTTOM");
  230. }
  231. else if (_stereoRendering == StereoPacking.LeftRight)
  232. {
  233. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_LEFT");
  234. _cubemapToEquirectangularMaterial.EnableKeyword("STEREOPACK_RIGHT");
  235. }
  236. _finalTarget.DiscardContents();
  237. Graphics.Blit(_cubeTarget, _finalTarget, _cubemapToEquirectangularMaterial);
  238. // Restore camera state
  239. camera.transform.localPosition = cameraPosition;
  240. }
  241. }
  242. private void RenderCameraToCubemap(Camera camera, RenderTexture cubemapTarget)
  243. {
  244. // Cache old camera values
  245. float prevFieldOfView = camera.fieldOfView;
  246. RenderTexture prevtarget = camera.targetTexture;
  247. Quaternion prevRotation = camera.transform.rotation;
  248. // Ignore the camera rotation
  249. Quaternion xform = camera.transform.rotation;
  250. if (!_supportCameraRotation)
  251. {
  252. xform = Quaternion.identity;
  253. }
  254. // NOTE: There is a bug in Unity 2017.1.0f3 to at least 2017.2beta7 which causes deferred rendering mode to always clear the cubemap target to white
  255. camera.targetTexture = _faceTarget;
  256. camera.fieldOfView = 90f;
  257. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.forward, Vector3.down);
  258. _faceTarget.DiscardContents();
  259. camera.Render();
  260. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.PositiveZ);
  261. Graphics.Blit(_faceTarget, _blitMaterial);
  262. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.back, Vector3.down);
  263. _faceTarget.DiscardContents();
  264. camera.Render();
  265. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.NegativeZ);
  266. Graphics.Blit(_faceTarget, _blitMaterial);
  267. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.right, Vector3.down);
  268. _faceTarget.DiscardContents();
  269. camera.Render();
  270. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.NegativeX);
  271. Graphics.Blit(_faceTarget, _blitMaterial);
  272. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.left, Vector3.down);
  273. _faceTarget.DiscardContents();
  274. camera.Render();
  275. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.PositiveX);
  276. Graphics.Blit(_faceTarget, _blitMaterial);
  277. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.up, Vector3.forward);
  278. _faceTarget.DiscardContents();
  279. camera.Render();
  280. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.PositiveY);
  281. Graphics.Blit(_faceTarget, _blitMaterial);
  282. camera.transform.rotation = xform * Quaternion.LookRotation(Vector3.down, Vector3.back);
  283. _faceTarget.DiscardContents();
  284. camera.Render();
  285. Graphics.SetRenderTarget(cubemapTarget, 0, CubemapFace.NegativeY);
  286. Graphics.Blit(_faceTarget, _blitMaterial);
  287. Graphics.SetRenderTarget(null);
  288. // Restore camera values
  289. camera.transform.rotation = prevRotation;
  290. camera.fieldOfView = prevFieldOfView;
  291. camera.targetTexture = prevtarget;
  292. }
  293. private void LateUpdate()
  294. {
  295. if (_motionBlur != null)
  296. {
  297. if (_capturing && !_paused && _handle >= 0)
  298. {
  299. UpdateTexture();
  300. _motionBlur.Accumulate(_finalTarget);
  301. }
  302. }
  303. }
  304. public override bool PrepareCapture()
  305. {
  306. if (_capturing)
  307. {
  308. return false;
  309. }
  310. if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("emulated"))
  311. {
  312. Debug.LogError("[AVProMovieCapture] OpenGL not yet supported for CaptureFromCamera360 component, please use Direct3D11 instead. You may need to switch your build platform to Windows.");
  313. return false;
  314. }
  315. // Setup material
  316. _pixelFormat = NativePlugin.PixelFormat.RGBA32;
  317. _isTopDown = true;
  318. if (_camera == null)
  319. {
  320. _camera = this.GetComponent<Camera>();
  321. }
  322. if (_camera == null)
  323. {
  324. Debug.LogError("[AVProMovieCapture] No camera assigned to CaptureFromCamera360");
  325. return false;
  326. }
  327. // Resolution
  328. int finalWidth = Mathf.FloorToInt(_camera.pixelRect.width);
  329. int finalHeight = Mathf.FloorToInt(_camera.pixelRect.height);
  330. if (_renderResolution == Resolution.Custom)
  331. {
  332. finalWidth = (int)_renderSize.x;
  333. finalHeight = (int)_renderSize.y;
  334. }
  335. else if (_renderResolution != Resolution.Original)
  336. {
  337. GetResolution(_renderResolution, ref finalWidth, ref finalHeight);
  338. }
  339. // Setup rendering a different render target if we're overriding resolution or anti-aliasing
  340. //if (_renderResolution != Resolution.Original || _renderAntiAliasing != QualitySettings.antiAliasing)
  341. {
  342. int aaLevel = GetCameraAntiAliasingLevel(_camera);
  343. if (!Mathf.IsPowerOfTwo(_cubemapResolution))
  344. {
  345. _cubemapResolution = Mathf.ClosestPowerOfTwo(_cubemapResolution);
  346. Debug.LogWarning("[AVProMovieCapture] Cubemap must be power-of-2 dimensions, resizing to closest = " + _cubemapResolution);
  347. }
  348. // Create the final render target
  349. _targetNativePointer = System.IntPtr.Zero;
  350. if (_finalTarget != null)
  351. {
  352. _finalTarget.DiscardContents();
  353. if (_finalTarget.width != finalWidth || _finalTarget.height != finalHeight)
  354. {
  355. RenderTexture.ReleaseTemporary(_finalTarget);
  356. _finalTarget = null;
  357. }
  358. }
  359. if (_finalTarget == null)
  360. {
  361. _finalTarget = RenderTexture.GetTemporary(finalWidth, finalHeight, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Default, 1);
  362. _finalTarget.name = "[AVProMovieCapture] 360 Final Target";
  363. }
  364. // Create the per-face render target (only when need to support GUI rendering)
  365. if (_faceTarget != null)
  366. {
  367. _faceTarget.DiscardContents();
  368. if (_faceTarget.width != _cubemapResolution || _faceTarget.height != _cubemapResolution || _faceTarget.depth != _cubemapDepth || aaLevel != _faceTarget.antiAliasing)
  369. {
  370. RenderTexture.Destroy(_faceTarget);
  371. _faceTarget = null;
  372. }
  373. }
  374. if (IsManualCubemapRendering())
  375. {
  376. if (_faceTarget == null)
  377. {
  378. _faceTarget = new RenderTexture(_cubemapResolution, _cubemapResolution, _cubemapDepth, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
  379. _faceTarget.name = "[AVProMovieCapture] 360 Face Target";
  380. _faceTarget.isPowerOfTwo = true;
  381. _faceTarget.wrapMode = TextureWrapMode.Clamp;
  382. _faceTarget.filterMode = FilterMode.Bilinear;
  383. _faceTarget.autoGenerateMips = false;
  384. _faceTarget.antiAliasing = aaLevel;
  385. }
  386. _cubemapToEquirectangularMaterial.SetFloat(_propFlipX, 0.0f);
  387. }
  388. else
  389. {
  390. // Unity's RenderToCubemap result needs flipping
  391. _cubemapToEquirectangularMaterial.SetFloat(_propFlipX, 1.0f);
  392. }
  393. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_TOP");
  394. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_BOTTOM");
  395. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_LEFT");
  396. _cubemapToEquirectangularMaterial.DisableKeyword("STEREOPACK_RIGHT");
  397. // Create the cube render target
  398. int cubeDepth = 0;
  399. if (!IsManualCubemapRendering())
  400. {
  401. cubeDepth = _cubemapDepth;
  402. }
  403. int cubeAA = 1;
  404. if (!IsManualCubemapRendering())
  405. {
  406. cubeAA = aaLevel;
  407. }
  408. if (_cubeTarget != null)
  409. {
  410. _cubeTarget.DiscardContents();
  411. if (_cubeTarget.width != _cubemapResolution || _cubeTarget.height != _cubemapResolution || _cubeTarget.depth != cubeDepth || cubeAA != _cubeTarget.antiAliasing)
  412. {
  413. RenderTexture.Destroy(_cubeTarget);
  414. _cubeTarget = null;
  415. }
  416. }
  417. if (_cubeTarget == null)
  418. {
  419. _cubeTarget = new RenderTexture(_cubemapResolution, _cubemapResolution, cubeDepth, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
  420. _cubeTarget.name = "[AVProMovieCapture] 360 Cube Target";
  421. _cubeTarget.isPowerOfTwo = true;
  422. #if AVPRO_MOVIECAPTURE_RENDERTEXTUREDIMENSIONS_54
  423. _cubeTarget.dimension = UnityEngine.Rendering.TextureDimension.Cube;
  424. #else
  425. _cubeTarget.isCubemap = true;
  426. #endif
  427. _cubeTarget.useMipMap = false;
  428. _cubeTarget.autoGenerateMips = false;
  429. _cubeTarget.antiAliasing = cubeAA;
  430. _cubeTarget.wrapMode = TextureWrapMode.Clamp;
  431. _cubeTarget.filterMode = FilterMode.Bilinear;
  432. }
  433. if (_useMotionBlur)
  434. {
  435. _motionBlurCameras = new Camera[1];
  436. _motionBlurCameras[0] = _camera;
  437. }
  438. }
  439. SelectRecordingResolution(finalWidth, finalHeight);
  440. GenerateFilename();
  441. return base.PrepareCapture();
  442. }
  443. public override Texture GetPreviewTexture()
  444. {
  445. if (IsUsingMotionBlur())
  446. {
  447. return _motionBlur.FinalTexture;
  448. }
  449. return _finalTarget;
  450. }
  451. public override void Start()
  452. {
  453. Shader shader = Resources.Load<Shader>("CubemapToEquirectangular");
  454. if (shader != null)
  455. {
  456. _cubemapToEquirectangularMaterial = new Material(shader);
  457. }
  458. else
  459. {
  460. Debug.LogError("[AVProMovieCapture] Can't find CubemapToEquirectangular shader");
  461. }
  462. Shader blitShader = Shader.Find("Hidden/BlitCopy");
  463. if (blitShader != null)
  464. {
  465. _blitMaterial = new Material(blitShader);
  466. }
  467. else
  468. {
  469. Debug.LogError("[AVProMovieCapture] Can't find Hidden/BlitCopy shader");
  470. }
  471. _propFlipX = Shader.PropertyToID("_FlipX");
  472. base.Start();
  473. }
  474. public override void OnDestroy()
  475. {
  476. _targetNativePointer = System.IntPtr.Zero;
  477. if (_blitMaterial != null)
  478. {
  479. Material.Destroy(_blitMaterial);
  480. _blitMaterial = null;
  481. }
  482. if (_faceTarget != null)
  483. {
  484. RenderTexture.Destroy(_faceTarget);
  485. _faceTarget = null;
  486. }
  487. if (_cubeTarget != null)
  488. {
  489. RenderTexture.Destroy(_cubeTarget);
  490. _cubeTarget = null;
  491. }
  492. if (_finalTarget != null)
  493. {
  494. RenderTexture.ReleaseTemporary(_finalTarget);
  495. _finalTarget = null;
  496. }
  497. base.OnDestroy();
  498. }
  499. }
  500. }