void Start() { var ctlr = Controller; if (ctlr == null) { Debug.LogError("NvrEye must be child of a StereoController."); enabled = false; return; } // Save reference to the found controller and it's camera. controller = ctlr; monoCamera = controller.GetComponent <Camera>(); SetupStereo(/*forceUpdate=*/ true); }
/// Helper to copy camera settings from the controller's mono camera. Used in SetupStereo() and /// in the custom editor for StereoController. The parameters parx and pary, if not left at /// default, should come from a projection matrix returned by the SDK. They affect the apparent /// depth of the camera's window. See SetupStereo(). public void CopyCameraAndMakeSideBySide(NvrStereoController controller, float parx = 0, float pary = 0) { #if UNITY_EDITOR // Member variable 'cam' not always initialized when this method called in Editor. // So, we'll just make a local of the same name. var cam = GetComponent <Camera>(); #endif // Same for controller's camera, but it can happen at runtime too (via AddStereoRig on // StereoController). var monoCamera = controller == this.controller ? this.monoCamera : controller.GetComponent <Camera>(); float ipd = NvrProfile.Default.viewer.lenses.separation * controller.stereoMultiplier; Vector3 localPosition = Application.isPlaying ? transform.localPosition : (eye == NvrViewer.Eye.Left ? -ipd / 2 : ipd / 2) * Vector3.right;; // Sync the camera properties. cam.CopyFrom(monoCamera); cam.cullingMask ^= toggleCullingMask.value; // Not sure why we have to do this, but if we don't then switching between drawing to // the main screen or to the stereo rendertexture acts very strangely. cam.depth = eye == NvrViewer.Eye.Left ? monoCamera.depth + 1 : monoCamera.depth + 2; // Reset transform, which was clobbered by the CopyFrom() call. // Since we are a child of the mono camera, we inherit its transform already. transform.localPosition = localPosition; transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one; Skybox monoCameraSkybox = monoCamera.GetComponent <Skybox>(); Skybox customSkybox = GetComponent <Skybox>(); if (monoCameraSkybox != null) { if (customSkybox == null) { customSkybox = gameObject.AddComponent <Skybox>(); } customSkybox.material = monoCameraSkybox.material; } else if (customSkybox != null) { Destroy(customSkybox); } // Set up side-by-side stereo. // Note: The code is written this way so that non-fullscreen cameras // (PIP: picture-in-picture) still work in stereo. Even if the PIP's content is // not going to be in stereo, the PIP itself still has to be rendered in both eyes. Rect rect = cam.rect; // Move away from edges if padding requested. Some HMDs make the edges of the // screen a bit hard to see. Vector2 center = rect.center; center.x = Mathf.Lerp(center.x, 0.5f, 0); center.y = Mathf.Lerp(center.y, 0.5f, 0); rect.center = center; // Semi-hacky aspect ratio adjustment because the screen is only half as wide due // to side-by-side stereo, to make sure the PIP width fits. float width = Mathf.SmoothStep(-0.5f, 0.5f, (rect.width + 1) / 2); rect.x += (rect.width - width) / 2; rect.width = width; // Divide the outside region of window proportionally in each half of the screen. rect.x *= (0.5f - rect.width) / (1 - rect.width); if (eye == NvrViewer.Eye.Right) { rect.x += 0.5f; // Move to right half of the screen. } // Adjust the window for requested parallax. This affects the apparent depth of the // window in the main camera's screen. Useful for PIP windows only, where rect.width < 1. float parallax = Mathf.Clamp01(0); if (monoCamera.rect.width < 1 && parallax > 0) { // Note: parx and pary are signed, with opposite signs in each eye. rect.x -= parx / 4 * parallax; // Extra factor of 1/2 because of side-by-side stereo. rect.y -= pary / 2 * parallax; } if (NvrViewer.USE_DTR && NvrGlobal.supportDtr && Application.platform == RuntimePlatform.Android) { // DTR&DFT的Android模式左右眼视窗大小均为0~1 rect = new Rect(0, 0, 1, 1); } cam.rect = rect; // cam.backgroundColor = eye == NvrViewer.Eye.Left ? Color.green : Color.blue; }