CaptureFromCamera.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. #if UNITY_EDITOR && !UNITY_2018_3_OR_NEWER
  2. #define SUPPORT_SCENE_VIEW_GIZMOS_CAPTURE
  3. #endif
  4. using UnityEngine;
  5. using System.Collections;
  6. //-----------------------------------------------------------------------------
  7. // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. namespace RenderHeads.Media.AVProMovieCapture
  10. {
  11. /// <summary>
  12. /// Capture from a specific Unity camera. This component re-renders the camera manually, so it does add extra draw calls.
  13. /// </summary>
  14. //[RequireComponent(typeof(Camera))]
  15. [AddComponentMenu("AVPro Movie Capture/Capture From Camera", 1)]
  16. public class CaptureFromCamera : CaptureBase
  17. {
  18. [SerializeField] CameraSelector _cameraSelector = null;
  19. [SerializeField] Camera _lastCamera = null;
  20. [SerializeField] Camera[] _contribCameras = null;
  21. [SerializeField] bool _useContributingCameras = true;
  22. #if SUPPORT_SCENE_VIEW_GIZMOS_CAPTURE
  23. [Tooltip("When capturing from the Scene View camera, whether to include rendering of Gizmos or not. If Gizmos are included then resolution and AA options cannot be overridde and will use the current Scene View window settings")]
  24. [SerializeField] bool _includeSceneViewGizmos = true;
  25. #endif
  26. private RenderTexture _target;
  27. private RenderTexture _resolveTexture;
  28. private System.IntPtr _targetNativePointer = System.IntPtr.Zero;
  29. private Texture _targetNativeTexture;
  30. private Texture _previewTexture;
  31. public CameraSelector CameraSelector
  32. {
  33. get { return _cameraSelector; }
  34. set { _cameraSelector = value; }
  35. }
  36. public bool UseContributingCameras
  37. {
  38. get { return _useContributingCameras; }
  39. set { _useContributingCameras = value; }
  40. }
  41. public void SetCamera(Camera topCamera, bool useContributingCameras = true)
  42. {
  43. _lastCamera = topCamera;
  44. _contribCameras = null;
  45. _useContributingCameras = useContributingCameras;
  46. if (_useContributingCameras && _lastCamera != null)
  47. {
  48. if (Utils.HasContributingCameras(_lastCamera))
  49. {
  50. _contribCameras = Utils.FindContributingCameras(topCamera);
  51. }
  52. }
  53. }
  54. public void SetCamera(Camera topCamera, Camera[] contributingCameras)
  55. {
  56. _lastCamera = topCamera;
  57. _contribCameras = contributingCameras;
  58. }
  59. private bool RequiresResolve(Texture texture)
  60. {
  61. #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || (UNITY_IOS && !UNITY_EDITOR)
  62. // Texture resolve wholly unnecessary on macOS and iOS
  63. return false;
  64. #else
  65. bool result = false;
  66. if (texture is RenderTexture)
  67. {
  68. RenderTexture rt = texture as RenderTexture;
  69. // Linear textures require resolving to sRGB
  70. result = !rt.sRGB;
  71. if (!result &&
  72. (rt.format != RenderTextureFormat.ARGB32) &&
  73. (rt.format != RenderTextureFormat.Default) &&
  74. (rt.format != RenderTextureFormat.BGRA32)
  75. )
  76. {
  77. // Exotic texture formats require resolving
  78. result = true;
  79. }
  80. }
  81. else
  82. {
  83. // Any other texture type needs to be resolve to RenderTexture
  84. result = true;
  85. }
  86. return result;
  87. #endif
  88. }
  89. private bool HasCamera()
  90. {
  91. return (_lastCamera != null);
  92. }
  93. private bool HasContributingCameras()
  94. {
  95. return (_useContributingCameras && _contribCameras != null && _contribCameras.Length > 0);
  96. }
  97. public override void UpdateFrame()
  98. {
  99. // Detect changes from camera selector
  100. if (_cameraSelector != null)
  101. {
  102. if (_lastCamera != _cameraSelector.Camera)
  103. {
  104. SetCamera(_cameraSelector.Camera, _useContributingCameras);
  105. }
  106. }
  107. if (_useWaitForEndOfFrame)
  108. {
  109. if (_capturing && !_paused && HasCamera())
  110. {
  111. StartCoroutine(FinalRenderCapture());
  112. }
  113. base.UpdateFrame();
  114. }
  115. else
  116. {
  117. base.UpdateFrame();
  118. Capture();
  119. }
  120. }
  121. #if EXPERIMENTAL_FRAME_PAUSE
  122. private bool _stillBusy = false;
  123. private IEnumerator FinalRenderCapture()
  124. {
  125. if (_stillBusy)
  126. {
  127. yield return null;
  128. }
  129. else
  130. {
  131. _stillBusy = true;
  132. Time.timeScale = 0.0f;
  133. yield return new WaitUntil(CanContinue);
  134. Time.timeScale = 1.0f;
  135. yield return _waitForEndOfFrame;
  136. yield return Capture();
  137. _stillBusy = false;
  138. }
  139. }
  140. #else
  141. private IEnumerator FinalRenderCapture()
  142. {
  143. yield return _waitForEndOfFrame;
  144. yield return Capture();
  145. }
  146. #endif
  147. // If we're forcing a resolution or AA change then we have to render the camera again to the new target
  148. // If we try to just set the targetTexture of the camera and grab it in OnRenderImage we can't render it to the screen as before :(
  149. public IEnumerator Capture()
  150. {
  151. TickFrameTimer();
  152. if (_capturing && !_paused && HasCamera())
  153. {
  154. bool canGrab = true;
  155. if (IsUsingMotionBlur())
  156. {
  157. // If the motion blur is still accumulating, don't grab this frame
  158. canGrab = _motionBlur.IsFrameAccumulated;
  159. }
  160. if (canGrab)
  161. {
  162. /*while (_handle >= 0 && !AVProMovieCapturePlugin.IsNewFrameDue(_handle))
  163. {
  164. System.Threading.Thread.Sleep(1);
  165. }*/
  166. if (_handle >= 0 && CanOutputFrame())
  167. {
  168. RenderTexture sourceTexture = _target;
  169. // In 2018.3 and above a different method is used to render the gizmos etc, so we don't yet support capturing these
  170. #if SUPPORT_SCENE_VIEW_GIZMOS_CAPTURE
  171. // Support capturing the Scene View target texture directly so that we get all gizmo rendering
  172. if (_lastCamera != null && _includeSceneViewGizmos && (_lastCamera.hideFlags & HideFlags.NotEditable) != 0 && _lastCamera.targetTexture != null)
  173. {
  174. sourceTexture = _lastCamera.targetTexture;
  175. }
  176. #endif
  177. if (sourceTexture == _target)
  178. {
  179. // Render the camera(s)
  180. if (!IsUsingMotionBlur())
  181. {
  182. UpdateTexture();
  183. }
  184. else
  185. {
  186. // Just grab the last result of the motion blur
  187. _target.DiscardContents();
  188. Graphics.Blit(_motionBlur.FinalTexture, _target);
  189. }
  190. }
  191. // If the texture isn't suitable then blit it to the Rendertexture so the native plugin can grab it
  192. if (RequiresResolve(sourceTexture))
  193. {
  194. CreateResolveTexture(sourceTexture.width, sourceTexture.height);
  195. _resolveTexture.DiscardContents();
  196. // Between Unity 2018.1.0 and 2018.3.0 Unity doesn't seem to set the correct sRGBWrite state and keeps it as false
  197. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  198. bool sRGBWritePrev = GL.sRGBWrite;
  199. GL.sRGBWrite = true;
  200. #endif
  201. Graphics.Blit(sourceTexture, _resolveTexture);
  202. sourceTexture = _resolveTexture;
  203. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  204. GL.sRGBWrite = sRGBWritePrev;
  205. #endif
  206. }
  207. // Side-by-side transparency
  208. RenderTexture newSourceTexture = UpdateForSideBySideTransparency( sourceTexture );
  209. if( newSourceTexture )
  210. {
  211. sourceTexture = newSourceTexture;
  212. }
  213. if (_supportTextureRecreate || _targetNativeTexture != sourceTexture)
  214. {
  215. // NOTE: If support for captures to survive through alt-tab events, or window resizes where the GPU resources are recreated
  216. // is required, then this line is needed. It is very expensive though as it does a sync with the rendering thread.
  217. _targetNativePointer = sourceTexture.GetNativeTexturePtr();
  218. _targetNativeTexture = sourceTexture;
  219. }
  220. _previewTexture = sourceTexture;
  221. NativePlugin.SetTexturePointer(_handle, _targetNativePointer);
  222. RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
  223. EncodeUnityAudio();
  224. UpdateFPS();
  225. }
  226. }
  227. }
  228. base.UpdateFrame();
  229. RenormTimer();
  230. yield break;
  231. }
  232. private bool RequiresHDR()
  233. {
  234. // Check if any of the cameras in the chain are set to allow HDR
  235. bool result = _lastCamera.allowHDR;
  236. if (!result && HasContributingCameras())
  237. {
  238. for (int cameraIndex = 0; cameraIndex < _contribCameras.Length; cameraIndex++)
  239. {
  240. Camera camera = _contribCameras[cameraIndex];
  241. if (camera != null && camera.isActiveAndEnabled)
  242. {
  243. result = camera.allowHDR;
  244. if (result)
  245. {
  246. break;
  247. }
  248. }
  249. }
  250. }
  251. return result;
  252. }
  253. private void UpdateTexture()
  254. {
  255. // Between Unity 2018.1.0 and 2018.3.0 Unity doesn't seem to set the correct sRGBWrite state and keeps it as false
  256. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  257. bool sRGBWritePrev = GL.sRGBWrite;
  258. GL.sRGBWrite = _target.sRGB;
  259. #endif
  260. // Render a single camera
  261. if (!HasContributingCameras())
  262. {
  263. RenderTexture prev = _lastCamera.targetTexture;
  264. // Reset the viewport rect as we're rendering to a texture captures the full viewport
  265. Rect prevRect = _lastCamera.rect;
  266. CameraClearFlags prevClear = _lastCamera.clearFlags;
  267. Color prevColor = _lastCamera.backgroundColor;
  268. bool clearChanged = false;
  269. if (_lastCamera.clearFlags == CameraClearFlags.Nothing || _lastCamera.clearFlags == CameraClearFlags.Depth)
  270. {
  271. clearChanged = true;
  272. _lastCamera.clearFlags = CameraClearFlags.SolidColor;
  273. if (_Transparency == Transparency.Codec)
  274. {
  275. _lastCamera.backgroundColor = new Color(0f, 0f, 0f, 0f);
  276. }
  277. else
  278. {
  279. _lastCamera.backgroundColor = Color.black;
  280. }
  281. }
  282. // Render
  283. _lastCamera.rect = new Rect(0f, 0f, 1f, 1f);
  284. _lastCamera.targetTexture = _target;
  285. _lastCamera.Render();
  286. // Restore camera
  287. {
  288. _lastCamera.rect = prevRect;
  289. if (clearChanged)
  290. {
  291. _lastCamera.clearFlags = prevClear;
  292. _lastCamera.backgroundColor = prevColor;
  293. }
  294. _lastCamera.targetTexture = prev;
  295. }
  296. }
  297. // Render the camera chain
  298. else
  299. {
  300. // First render contributing cameras
  301. for (int cameraIndex = 0; cameraIndex < _contribCameras.Length; cameraIndex++)
  302. {
  303. Camera camera = _contribCameras[cameraIndex];
  304. if (camera != null && camera.isActiveAndEnabled)
  305. {
  306. RenderTexture prev = camera.targetTexture;
  307. camera.targetTexture = _target;
  308. camera.Render();
  309. camera.targetTexture = prev;
  310. }
  311. }
  312. // Finally render the last camera
  313. if (_lastCamera != null)
  314. {
  315. RenderTexture prev = _lastCamera.targetTexture;
  316. _lastCamera.targetTexture = _target;
  317. _lastCamera.Render();
  318. _lastCamera.targetTexture = prev;
  319. }
  320. }
  321. //UnityEditor.Selection.activeObject = _lastCamera.targetTexture;
  322. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  323. GL.sRGBWrite = sRGBWritePrev;
  324. #endif
  325. }
  326. #if false
  327. // NOTE: This is old code based on OnRenderImage...may be revived at some point
  328. private void OnRenderImage(RenderTexture source, RenderTexture dest)
  329. {
  330. if (_capturing && !_paused)
  331. {
  332. while (_handle >= 0 && !NativePlugin.IsNewFrameDue(_handle))
  333. {
  334. System.Threading.Thread.Sleep(1);
  335. }
  336. if (_handle >= 0)
  337. {
  338. if (_audioCapture && _audioDeviceIndex < 0 && !_noAudio && _isRealTime)
  339. {
  340. int audioDataLength = 0;
  341. System.IntPtr audioDataPtr = _audioCapture.ReadData(out audioDataLength);
  342. if (audioDataLength > 0)
  343. {
  344. NativePlugin.EncodeAudio(_handle, audioDataPtr, (uint)audioDataLength);
  345. }
  346. }
  347. // In Direct3D the RT can be flipped vertically
  348. /*if (source.texelSize.y < 0)
  349. {
  350. }*/
  351. Graphics.Blit(source, dest);
  352. _lastSource = source;
  353. _lastDest = dest;
  354. if (dest != _originalTarget)
  355. {
  356. Graphics.Blit(dest, _originalTarget);
  357. }
  358. #if AVPRO_MOVIECAPTURE_GLISSUEEVENT_52
  359. GL.IssuePluginEvent(NativePlugin.GetRenderEventFunc(), NativePlugin.PluginID | (int)NativePlugin.PluginEvent.CaptureFrameBuffer | _handle);
  360. #else
  361. GL.IssuePluginEvent(NativePlugin.PluginID | (int)NativePlugin.PluginEvent.CaptureFrameBuffer | _handle);
  362. #endif
  363. GL.InvalidateState();
  364. UpdateFPS();
  365. return;
  366. }
  367. }
  368. // Pass-through
  369. Graphics.Blit(source, dest);
  370. _lastSource = source;
  371. _lastDest = dest;
  372. }
  373. #endif
  374. public override void UnprepareCapture()
  375. {
  376. NativePlugin.SetTexturePointer(_handle, System.IntPtr.Zero);
  377. if (_target != null)
  378. {
  379. _target.DiscardContents();
  380. }
  381. _targetNativePointer = System.IntPtr.Zero;
  382. _targetNativeTexture = null;
  383. _previewTexture = null;
  384. base.UnprepareCapture();
  385. }
  386. private void CreateResolveTexture(int width, int height)
  387. {
  388. if (_resolveTexture != null)
  389. {
  390. if (_resolveTexture.width != width ||
  391. _resolveTexture.height != height)
  392. {
  393. RenderTexture.ReleaseTemporary(_resolveTexture);
  394. _resolveTexture = null;
  395. }
  396. }
  397. if (_resolveTexture == null)
  398. {
  399. _resolveTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
  400. _resolveTexture.Create();
  401. }
  402. if (_resolveTexture != null)
  403. {
  404. _targetNativePointer = _resolveTexture.GetNativeTexturePtr();
  405. _targetNativeTexture = _resolveTexture;
  406. }
  407. }
  408. public override Texture GetPreviewTexture()
  409. {
  410. return _previewTexture;
  411. }
  412. public override bool PrepareCapture()
  413. {
  414. if (_capturing)
  415. {
  416. return false;
  417. }
  418. #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
  419. if (SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 9"))
  420. {
  421. Debug.LogError("[AVProMovieCapture] Direct3D9 not yet supported, please use Direct3D11 instead.");
  422. return false;
  423. }
  424. else if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("emulated"))
  425. {
  426. Debug.LogError("[AVProMovieCapture] OpenGL not yet supported for CaptureFromCamera component, please use Direct3D11 instead. You may need to switch your build platform to Windows.");
  427. return false;
  428. }
  429. #endif
  430. // Setup material
  431. _pixelFormat = NativePlugin.PixelFormat.RGBA32;
  432. _isTopDown = true;
  433. _Transparency = Transparency.None;
  434. if( !NativePlugin.IsBasicEdition() )
  435. {
  436. if( (_outputTarget == OutputTarget.VideoFile || _outputTarget == OutputTarget.NamedPipe) && GetEncoderHints().videoHints.transparency != Transparency.None )
  437. {
  438. _Transparency = GetEncoderHints().videoHints.transparency;
  439. }
  440. else if( _outputTarget == OutputTarget.ImageSequence && GetEncoderHints().imageHints.transparency != Transparency.None )
  441. {
  442. _Transparency = GetEncoderHints().imageHints.transparency;
  443. }
  444. /*
  445. _supportTransparency = ((_outputTarget == OutputTarget.VideoFile || _outputTarget == OutputTarget.NamedPipe) && GetEncoderHints().videoHints.supportTransparency) ||
  446. (_outputTarget == OutputTarget.ImageSequence && GetEncoderHints().imageHints.supportTransparency);
  447. */
  448. }
  449. if (!HasCamera())
  450. {
  451. if (_cameraSelector != null)
  452. {
  453. if (_lastCamera != _cameraSelector.Camera)
  454. {
  455. SetCamera(_cameraSelector.Camera, _useContributingCameras);
  456. }
  457. }
  458. if (!HasCamera())
  459. {
  460. SetCamera(this.GetComponent<Camera>(), _useContributingCameras);
  461. }
  462. if (!HasCamera())
  463. {
  464. SetCamera(Camera.main, _useContributingCameras);
  465. }
  466. if (!HasCamera())
  467. {
  468. Debug.LogError("[AVProMovieCapture] No camera assigned to CaptureFromCamera");
  469. return false;
  470. }
  471. }
  472. if (!HasContributingCameras() && (_lastCamera.clearFlags == CameraClearFlags.Depth || _lastCamera.clearFlags == CameraClearFlags.Nothing))
  473. {
  474. Debug.LogWarning("[AVProMovieCapture] This camera doesn't clear, consider setting contributing cameras");
  475. }
  476. int width = Mathf.FloorToInt(_lastCamera.pixelRect.width);
  477. int height = Mathf.FloorToInt(_lastCamera.pixelRect.height);
  478. // Setup rendering a different render target if we're overriding resolution or anti-aliasing
  479. {
  480. if (_renderResolution == Resolution.Custom)
  481. {
  482. width = (int)_renderSize.x;
  483. height = (int)_renderSize.y;
  484. }
  485. else if (_renderResolution != Resolution.Original)
  486. {
  487. GetResolution(_renderResolution, ref width, ref height);
  488. }
  489. int aaLevel = GetCameraAntiAliasingLevel(_lastCamera);
  490. // Create the render target
  491. if (_target != null)
  492. {
  493. _target.DiscardContents();
  494. if (_target.width != width || _target.height != height || _target.antiAliasing != aaLevel)
  495. {
  496. _targetNativePointer = System.IntPtr.Zero;
  497. _targetNativeTexture = null;
  498. RenderTexture.ReleaseTemporary(_target);
  499. _target = null;
  500. }
  501. }
  502. if (_target == null)
  503. {
  504. RenderTextureFormat textureFormat = Utils.GetBestRenderTextureFormat(RequiresHDR(), (_Transparency == Transparency.Codec), IsRealTime);
  505. _target = RenderTexture.GetTemporary(width, height, 24, textureFormat, RenderTextureReadWrite.Default, aaLevel);
  506. _target.name = "[AVProMovieCapture] Camera Target";
  507. _target.Create();
  508. }
  509. if (_target != null)
  510. {
  511. _targetNativePointer = _target.GetNativeTexturePtr();
  512. _targetNativeTexture = _target;
  513. }
  514. // Adjust size for camera rectangle
  515. /*if (camera.rect.width < 1f || camera.rect.height < 1f)
  516. {
  517. float rectWidth = Mathf.Clamp01(camera.rect.width + camera.rect.x) - Mathf.Clamp01(camera.rect.x);
  518. float rectHeight = Mathf.Clamp01(camera.rect.height + camera.rect.y) - Mathf.Clamp01(camera.rect.y);
  519. width = Mathf.FloorToInt(width * rectWidth);
  520. height = Mathf.FloorToInt(height * rectHeight);
  521. }*/
  522. if (_useMotionBlur)
  523. {
  524. _motionBlurCameras = new Camera[1];
  525. _motionBlurCameras[0] = _lastCamera;
  526. }
  527. }
  528. //
  529. int outputHeight = height;
  530. int outputWidth = width;
  531. if ( !NativePlugin.IsBasicEdition() )
  532. {
  533. if (_Transparency == Transparency.TopBottom || _Transparency == Transparency.LeftRight)
  534. {
  535. InitialiseSideBySideTransparency( width, height );
  536. }
  537. switch ( _Transparency )
  538. {
  539. case Transparency.TopBottom: outputHeight *= 2; break;
  540. case Transparency.LeftRight: outputWidth *= 2; break;
  541. }
  542. }
  543. SelectRecordingResolution(outputWidth, outputHeight);
  544. GenerateFilename();
  545. return base.PrepareCapture();
  546. }
  547. public override void OnDestroy()
  548. {
  549. if (_resolveTexture != null)
  550. {
  551. RenderTexture.ReleaseTemporary(_resolveTexture);
  552. _resolveTexture = null;
  553. }
  554. if (_target != null)
  555. {
  556. _targetNativePointer = System.IntPtr.Zero;
  557. RenderTexture.ReleaseTemporary(_target);
  558. _target = null;
  559. }
  560. base.OnDestroy();
  561. }
  562. }
  563. }