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;
    }