OVRSandwichComposition.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. using UnityEngine;
  2. using UnityEngine.Rendering;
  3. #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
  4. public class OVRSandwichComposition : OVRCameraComposition
  5. {
  6. public float frameRealtime;
  7. public Camera fgCamera;
  8. public Camera bgCamera;
  9. public class HistoryRecord
  10. {
  11. public float timestamp = float.MinValue;
  12. public RenderTexture fgRenderTexture;
  13. public RenderTexture bgRenderTexture;
  14. public RenderTexture boundaryMeshMaskTexture;
  15. }
  16. public readonly int historyRecordCount = 8; // enough to compensate 88ms latency @ 90 fps
  17. public readonly HistoryRecord[] historyRecordArray;
  18. public int historyRecordCursorIndex = 0;
  19. public GameObject cameraProxyPlane;
  20. public Camera compositionCamera;
  21. public OVRSandwichCompositionManager compositionManager;
  22. private int _cameraFramePlaneLayer = -1;
  23. // find an unnamed layer between 24..29
  24. public int cameraFramePlaneLayer
  25. {
  26. get
  27. {
  28. if (_cameraFramePlaneLayer < 0)
  29. {
  30. for (int i=24; i<=29; ++i)
  31. {
  32. string layerName = LayerMask.LayerToName(i);
  33. if (layerName == null || layerName.Length == 0)
  34. {
  35. _cameraFramePlaneLayer = i;
  36. break;
  37. }
  38. }
  39. if (_cameraFramePlaneLayer == -1)
  40. {
  41. Debug.LogWarning("Unable to find an unnamed layer between 24 and 29.");
  42. _cameraFramePlaneLayer = 25;
  43. }
  44. Debug.LogFormat("Set the CameraFramePlaneLayer in SandwichComposition to {0}. Please do NOT put any other gameobject in this layer.", _cameraFramePlaneLayer);
  45. }
  46. return _cameraFramePlaneLayer;
  47. }
  48. }
  49. public override OVRManager.CompositionMethod CompositionMethod() { return OVRManager.CompositionMethod.Sandwich; }
  50. public OVRSandwichComposition(GameObject parentObject, Camera mainCamera, OVRManager.CameraDevice cameraDevice, bool useDynamicLighting, OVRManager.DepthQuality depthQuality)
  51. : base(cameraDevice, useDynamicLighting, depthQuality)
  52. {
  53. frameRealtime = Time.realtimeSinceStartup;
  54. historyRecordCount = OVRManager.instance.sandwichCompositionBufferedFrames;
  55. if (historyRecordCount < 1)
  56. {
  57. Debug.LogWarning("Invalid sandwichCompositionBufferedFrames in OVRManager. It should be at least 1");
  58. historyRecordCount = 1;
  59. }
  60. if (historyRecordCount > 16)
  61. {
  62. Debug.LogWarning("The value of sandwichCompositionBufferedFrames in OVRManager is too big. It would consume a lot of memory. It has been override to 16");
  63. historyRecordCount = 16;
  64. }
  65. historyRecordArray = new HistoryRecord[historyRecordCount];
  66. for (int i=0; i<historyRecordCount; ++i)
  67. {
  68. historyRecordArray[i] = new HistoryRecord();
  69. }
  70. historyRecordCursorIndex = 0;
  71. GameObject fgObject = new GameObject("MRSandwichForegroundCamera");
  72. fgObject.transform.parent = parentObject.transform;
  73. fgCamera = fgObject.AddComponent<Camera>();
  74. fgCamera.depth = 200;
  75. fgCamera.clearFlags = CameraClearFlags.SolidColor;
  76. fgCamera.backgroundColor = Color.clear;
  77. fgCamera.cullingMask = mainCamera.cullingMask & (~OVRManager.instance.extraHiddenLayers);
  78. fgCamera.nearClipPlane = mainCamera.nearClipPlane;
  79. fgCamera.farClipPlane = mainCamera.farClipPlane;
  80. GameObject bgObject = new GameObject("MRSandwichBackgroundCamera");
  81. bgObject.transform.parent = parentObject.transform;
  82. bgCamera = bgObject.AddComponent<Camera>();
  83. bgCamera.depth = 100;
  84. bgCamera.clearFlags = mainCamera.clearFlags;
  85. bgCamera.backgroundColor = mainCamera.backgroundColor;
  86. bgCamera.cullingMask = mainCamera.cullingMask & (~OVRManager.instance.extraHiddenLayers);
  87. bgCamera.nearClipPlane = mainCamera.nearClipPlane;
  88. bgCamera.farClipPlane = mainCamera.farClipPlane;
  89. // Create cameraProxyPlane for clipping
  90. Debug.Assert(cameraProxyPlane == null);
  91. cameraProxyPlane = GameObject.CreatePrimitive(PrimitiveType.Quad);
  92. cameraProxyPlane.name = "MRProxyClipPlane";
  93. cameraProxyPlane.transform.parent = parentObject.transform;
  94. cameraProxyPlane.GetComponent<Collider>().enabled = false;
  95. cameraProxyPlane.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  96. Material clipMaterial = new Material(Shader.Find("Oculus/OVRMRClipPlane"));
  97. cameraProxyPlane.GetComponent<MeshRenderer>().material = clipMaterial;
  98. clipMaterial.SetColor("_Color", Color.clear);
  99. clipMaterial.SetFloat("_Visible", 0.0f);
  100. cameraProxyPlane.transform.localScale = new Vector3(1000, 1000, 1000);
  101. cameraProxyPlane.SetActive(true);
  102. OVRMRForegroundCameraManager foregroundCameraManager = fgCamera.gameObject.AddComponent<OVRMRForegroundCameraManager>();
  103. foregroundCameraManager.clipPlaneGameObj = cameraProxyPlane;
  104. GameObject compositionCameraObject = new GameObject("MRSandwichCaptureCamera");
  105. compositionCameraObject.transform.parent = parentObject.transform;
  106. compositionCamera = compositionCameraObject.AddComponent<Camera>();
  107. compositionCamera.stereoTargetEye = StereoTargetEyeMask.None;
  108. compositionCamera.depth = float.MaxValue;
  109. compositionCamera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
  110. compositionCamera.clearFlags = CameraClearFlags.Depth;
  111. compositionCamera.backgroundColor = mainCamera.backgroundColor;
  112. compositionCamera.cullingMask = 1 << cameraFramePlaneLayer;
  113. compositionCamera.nearClipPlane = mainCamera.nearClipPlane;
  114. compositionCamera.farClipPlane = mainCamera.farClipPlane;
  115. if (!hasCameraDeviceOpened)
  116. {
  117. Debug.LogError("Unable to open camera device " + cameraDevice);
  118. }
  119. else
  120. {
  121. Debug.Log("SandwichComposition activated : useDynamicLighting " + (useDynamicLighting ? "ON" : "OFF"));
  122. CreateCameraFramePlaneObject(parentObject, compositionCamera, useDynamicLighting);
  123. cameraFramePlaneObject.layer = cameraFramePlaneLayer;
  124. RefreshRenderTextures(mainCamera);
  125. compositionManager = compositionCamera.gameObject.AddComponent<OVRSandwichCompositionManager>();
  126. compositionManager.fgTexture = historyRecordArray[historyRecordCursorIndex].fgRenderTexture;
  127. compositionManager.bgTexture = historyRecordArray[historyRecordCursorIndex].bgRenderTexture;
  128. }
  129. }
  130. public override void Update(Camera mainCamera)
  131. {
  132. if (!hasCameraDeviceOpened)
  133. {
  134. return;
  135. }
  136. frameRealtime = Time.realtimeSinceStartup;
  137. ++historyRecordCursorIndex;
  138. if (historyRecordCursorIndex >= historyRecordCount)
  139. {
  140. historyRecordCursorIndex = 0;
  141. }
  142. if (!OVRPlugin.SetHandNodePoseStateLatency(OVRManager.instance.handPoseStateLatency))
  143. {
  144. Debug.LogWarning("HandPoseStateLatency is invalid. Expect a value between 0.0 to 0.5, get " + OVRManager.instance.handPoseStateLatency);
  145. }
  146. RefreshRenderTextures(mainCamera);
  147. bgCamera.clearFlags = mainCamera.clearFlags;
  148. bgCamera.backgroundColor = mainCamera.backgroundColor;
  149. bgCamera.cullingMask = mainCamera.cullingMask & (~OVRManager.instance.extraHiddenLayers);
  150. fgCamera.cullingMask = mainCamera.cullingMask & (~OVRManager.instance.extraHiddenLayers);
  151. if (OVRMixedReality.useFakeExternalCamera || OVRPlugin.GetExternalCameraCount() == 0)
  152. {
  153. OVRPose worldSpacePose = new OVRPose();
  154. OVRPose trackingSpacePose = new OVRPose();
  155. trackingSpacePose.position = OVRMixedReality.fakeCameraPositon;
  156. trackingSpacePose.orientation = OVRMixedReality.fakeCameraRotation;
  157. worldSpacePose = OVRExtensions.ToWorldSpacePose(trackingSpacePose);
  158. RefreshCameraPoses(OVRMixedReality.fakeCameraFov, OVRMixedReality.fakeCameraAspect, worldSpacePose);
  159. }
  160. else
  161. {
  162. OVRPlugin.CameraExtrinsics extrinsics;
  163. OVRPlugin.CameraIntrinsics intrinsics;
  164. // So far, only support 1 camera for MR and always use camera index 0
  165. if (OVRPlugin.GetMixedRealityCameraInfo(0, out extrinsics, out intrinsics))
  166. {
  167. OVRPose worldSpacePose = ComputeCameraWorldSpacePose(extrinsics);
  168. float fovY = Mathf.Atan(intrinsics.FOVPort.UpTan) * Mathf.Rad2Deg * 2;
  169. float aspect = intrinsics.FOVPort.LeftTan / intrinsics.FOVPort.UpTan;
  170. RefreshCameraPoses(fovY, aspect, worldSpacePose);
  171. }
  172. else
  173. {
  174. Debug.LogWarning("Failed to get external camera information");
  175. }
  176. }
  177. compositionCamera.GetComponent<OVRCameraFrameCompositionManager>().boundaryMeshMaskTexture = historyRecordArray[historyRecordCursorIndex].boundaryMeshMaskTexture;
  178. HistoryRecord record = GetHistoryRecordForComposition();
  179. UpdateCameraFramePlaneObject(mainCamera, compositionCamera, record.boundaryMeshMaskTexture);
  180. OVRSandwichCompositionManager compositionManager = compositionCamera.gameObject.GetComponent<OVRSandwichCompositionManager>();
  181. compositionManager.fgTexture = record.fgRenderTexture;
  182. compositionManager.bgTexture = record.bgRenderTexture;
  183. cameraProxyPlane.transform.position = fgCamera.transform.position + fgCamera.transform.forward * cameraFramePlaneDistance;
  184. cameraProxyPlane.transform.LookAt(cameraProxyPlane.transform.position + fgCamera.transform.forward);
  185. }
  186. public override void Cleanup()
  187. {
  188. base.Cleanup();
  189. Camera[] cameras = { fgCamera, bgCamera, compositionCamera };
  190. foreach (Camera c in cameras)
  191. {
  192. OVRCompositionUtil.SafeDestroy(c.gameObject);
  193. }
  194. fgCamera = null;
  195. bgCamera = null;
  196. compositionCamera = null;
  197. Debug.Log("SandwichComposition deactivated");
  198. }
  199. private RenderTextureFormat DesiredRenderTextureFormat(RenderTextureFormat originalFormat)
  200. {
  201. if (originalFormat == RenderTextureFormat.RGB565)
  202. {
  203. return RenderTextureFormat.ARGB1555;
  204. }
  205. else if (originalFormat == RenderTextureFormat.RGB111110Float)
  206. {
  207. return RenderTextureFormat.ARGBHalf;
  208. }
  209. else
  210. {
  211. return originalFormat;
  212. }
  213. }
  214. protected void RefreshRenderTextures(Camera mainCamera)
  215. {
  216. int width = Screen.width;
  217. int height = Screen.height;
  218. RenderTextureFormat format = mainCamera.targetTexture ? DesiredRenderTextureFormat(mainCamera.targetTexture.format) : RenderTextureFormat.ARGB32;
  219. int depth = mainCamera.targetTexture ? mainCamera.targetTexture.depth : 24;
  220. Debug.Assert(fgCamera != null && bgCamera != null);
  221. HistoryRecord record = historyRecordArray[historyRecordCursorIndex];
  222. record.timestamp = frameRealtime;
  223. if (record.fgRenderTexture == null || record.fgRenderTexture.width != width || record.fgRenderTexture.height != height || record.fgRenderTexture.format != format || record.fgRenderTexture.depth != depth)
  224. {
  225. record.fgRenderTexture = new RenderTexture(width, height, depth, format);
  226. record.fgRenderTexture.name = "Sandwich FG " + historyRecordCursorIndex.ToString();
  227. }
  228. fgCamera.targetTexture = record.fgRenderTexture;
  229. if (record.bgRenderTexture == null || record.bgRenderTexture.width != width || record.bgRenderTexture.height != height || record.bgRenderTexture.format != format || record.bgRenderTexture.depth != depth)
  230. {
  231. record.bgRenderTexture = new RenderTexture(width, height, depth, format);
  232. record.bgRenderTexture.name = "Sandwich BG " + historyRecordCursorIndex.ToString();
  233. }
  234. bgCamera.targetTexture = record.bgRenderTexture;
  235. if (OVRManager.instance.virtualGreenScreenType != OVRManager.VirtualGreenScreenType.Off)
  236. {
  237. if (record.boundaryMeshMaskTexture == null || record.boundaryMeshMaskTexture.width != width || record.boundaryMeshMaskTexture.height != height)
  238. {
  239. record.boundaryMeshMaskTexture = new RenderTexture(width, height, 0, RenderTextureFormat.R8);
  240. record.boundaryMeshMaskTexture.name = "Boundary Mask " + historyRecordCursorIndex.ToString();
  241. record.boundaryMeshMaskTexture.Create();
  242. }
  243. }
  244. else
  245. {
  246. record.boundaryMeshMaskTexture = null;
  247. }
  248. Debug.Assert(fgCamera.targetTexture != null && bgCamera.targetTexture != null && (OVRManager.instance.virtualGreenScreenType == OVRManager.VirtualGreenScreenType.Off || record.boundaryMeshMaskTexture != null));
  249. }
  250. protected HistoryRecord GetHistoryRecordForComposition()
  251. {
  252. float expectedTime = frameRealtime - OVRManager.instance.sandwichCompositionRenderLatency;
  253. int currIndex = historyRecordCursorIndex;
  254. int prevIndex = currIndex - 1;
  255. if (prevIndex < 0)
  256. {
  257. prevIndex = historyRecordCount - 1;
  258. }
  259. while (prevIndex != historyRecordCursorIndex)
  260. {
  261. if (historyRecordArray[prevIndex].timestamp <= expectedTime)
  262. {
  263. float timeToCurrIndex = historyRecordArray[currIndex].timestamp - expectedTime;
  264. float timeToPrevIndex = expectedTime - historyRecordArray[prevIndex].timestamp;
  265. return timeToCurrIndex <= timeToPrevIndex ? historyRecordArray[currIndex] : historyRecordArray[prevIndex];
  266. }
  267. currIndex = prevIndex;
  268. prevIndex = currIndex - 1;
  269. if (prevIndex < 0) prevIndex = historyRecordCount - 1;
  270. }
  271. // return the earliest frame
  272. return historyRecordArray[currIndex];
  273. }
  274. protected void RefreshCameraPoses(float fovY, float aspect, OVRPose pose)
  275. {
  276. Camera[] cameras = { fgCamera, bgCamera, compositionCamera };
  277. foreach (Camera c in cameras)
  278. {
  279. c.fieldOfView = fovY;
  280. c.aspect = aspect;
  281. c.transform.FromOVRPose(pose);
  282. }
  283. }
  284. public class OVRSandwichCompositionManager : MonoBehaviour
  285. {
  286. public RenderTexture fgTexture;
  287. public RenderTexture bgTexture;
  288. public Material alphaBlendMaterial;
  289. void Start()
  290. {
  291. Shader alphaBlendShader = Shader.Find("Oculus/UnlitTransparent");
  292. if (alphaBlendShader == null)
  293. {
  294. Debug.LogError("Unable to create transparent shader");
  295. return;
  296. }
  297. alphaBlendMaterial = new Material(alphaBlendShader);
  298. }
  299. private void OnPreRender()
  300. {
  301. if (fgTexture == null || bgTexture == null || alphaBlendMaterial == null)
  302. {
  303. Debug.LogError("OVRSandwichCompositionManager has not setup properly");
  304. return;
  305. }
  306. Graphics.Blit(bgTexture, RenderTexture.active);
  307. }
  308. void OnPostRender()
  309. {
  310. if (fgTexture == null || bgTexture == null || alphaBlendMaterial == null)
  311. {
  312. Debug.LogError("OVRSandwichCompositionManager has not setup properly");
  313. return;
  314. }
  315. Graphics.Blit(fgTexture, RenderTexture.active, alphaBlendMaterial);
  316. }
  317. }
  318. }
  319. #endif