Esempio n. 1
0
    // Helper to copy camera settings from the controller's mono camera.
    // Used in OnPreCull and 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 OnPreCull().
    public void CopyCameraAndMakeSideBySide(StereoController controller,
                                          float parx = 0, float pary = 0)
    {
        var camera = GetComponent<Camera>();

        // Sync the camera properties.
        camera.CopyFrom(controller.GetComponent<Camera>());
        camera.cullingMask ^= toggleCullingMask.value;

        // Reset transform, which was clobbered by the CopyFrom() call.
        // Since we are a child of the mono camera, we inherit its
        // transform already.
        // Use nominal IPD for the editor.  During play, OnPreCull() will
        // compute a real value.
        float ipd = CardboardProfile.Default.device.lenses.separation * controller.stereoMultiplier;
        transform.localPosition = (eye == Cardboard.Eye.Left ? -ipd/2 : ipd/2) * Vector3.right;
        transform.localRotation = Quaternion.identity;
        transform.localScale = Vector3.one;

        // 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 = camera.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, Mathf.Clamp01(controller.stereoPaddingX));
        center.y = Mathf.Lerp(center.y, 0.5f, Mathf.Clamp01(controller.stereoPaddingY));
        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 == Cardboard.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(controller.screenParallax);
        if (controller.GetComponent<Camera>().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;
        }

        camera.rect = rect;
    }
Esempio n. 2
0
 void Start()
 {
     var ctlr = Controller;
     if (ctlr == null) {
       Debug.LogError("GvrEye 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>();
     UpdateStereoValues();
 }
Esempio n. 3
0
    /// 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(StereoController 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 = GvrProfile.Default.viewer.lenses.separation * controller.stereoMultiplier;
        Vector3 localPosition = Application.isPlaying ?
        transform.localPosition : (eye == GvrViewer.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 = monoCamera.depth;

        // 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, Mathf.Clamp01(controller.stereoPaddingX));
        center.y = Mathf.Lerp(center.y, 0.5f, Mathf.Clamp01(controller.stereoPaddingY));
        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 == GvrViewer.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(controller.screenParallax);
        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;
        }

        cam.rect = rect;
    }
Esempio n. 4
0
    public void Render()
    {
        // Shouldn't happen because of the check in Start(), but just in case...
        if (controller == null)
        {
            return;
        }

        var       camera     = GetComponent <Camera>();
        var       monoCamera = controller.GetComponent <Camera>();
        Matrix4x4 proj       = Cardboard.SDK.Projection(eye);

        CopyCameraAndMakeSideBySide(controller, proj[0, 2], proj[1, 2]);

        // Zoom the stereo cameras if requested.
        float lerp = Mathf.Clamp01(controller.matchByZoom) * Mathf.Clamp01(controller.matchMonoFOV);
        // Lerping the reciprocal of proj(1,1) so zooming is linear in the frustum width, not the depth.
        float monoProj11 = monoCamera.projectionMatrix[1, 1];
        float zoom       = 1 / Mathf.Lerp(1 / proj[1, 1], 1 / monoProj11, lerp) / proj[1, 1];

        proj[0, 0] *= zoom;
        proj[1, 1] *= zoom;

        // Calculate stereo adjustments based on the center of interest.
        float ipdScale;
        float eyeOffset;

        controller.ComputeStereoAdjustment(proj[1, 1], transform.lossyScale.z,
                                           out ipdScale, out eyeOffset);

        // Set up the eye's view transform.
        transform.localPosition = ipdScale * Cardboard.SDK.EyeOffset(eye) +
                                  eyeOffset * Vector3.forward;

        // Set up the eye's projection.
        float near = monoCamera.nearClipPlane;
        float far  = monoCamera.farClipPlane;

        FixProjection(ref proj, near, far, ipdScale);
        camera.projectionMatrix = proj;

        if (Application.isEditor)
        {
            // So you can see the approximate frustum in the Scene view when the camera is selected.
            camera.fieldOfView = 2 * Mathf.Atan(1 / proj[1, 1]) * Mathf.Rad2Deg;
        }

        if (!Cardboard.SDK.nativeDistortionCorrection)
        {
            Matrix4x4 realProj = Cardboard.SDK.UndistortedProjection(eye);
            FixProjection(ref realProj, near, far, ipdScale);
            // Parts of the projection matrices that we need to convert texture coordinates between
            // distorted and undistorted frustums.  Include the transform between texture space [0..1]
            // and NDC [-1..1] (that's what the -1 and the /2 are for).  Also note that the zoom
            // factor is removed, because that will interfere with the distortion calculation.
            Vector4 projvec = new Vector4(proj[0, 0] / zoom, proj[1, 1] / zoom,
                                          proj[0, 2] - 1, proj[1, 2] - 1) / 2;
            Vector4 unprojvec = new Vector4(realProj[0, 0], realProj[1, 1],
                                            realProj[0, 2] - 1, realProj[1, 2] - 1) / 2;
            Shader.SetGlobalVector("_Projection", projvec);
            Shader.SetGlobalVector("_Unprojection", unprojvec);
            CardboardProfile p = Cardboard.SDK.Profile;
            Shader.SetGlobalVector("_Undistortion",
                                   new Vector4(p.device.inverse.k1, p.device.inverse.k2));
            Shader.SetGlobalVector("_Distortion",
                                   new Vector4(p.device.distortion.k1, p.device.distortion.k2));
        }

        RenderTexture stereoScreen = controller.StereoScreen;
        int           screenWidth  = stereoScreen ? stereoScreen.width : Screen.width;
        int           screenHeight = stereoScreen ? stereoScreen.height : Screen.height;

        if (stereoScreen == null)
        {
            // We are rendering straight to the screen.  Use the reported rect that is visible
            // through the device's lenses.
            Rect view = Cardboard.SDK.EyeRect(eye);
            Rect rect = camera.rect;
            if (eye == Cardboard.Eye.Right)
            {
                rect.x -= 0.5f;
            }
            rect.width  *= 2 * view.width;
            rect.x       = view.x + 2 * rect.x * view.width;
            rect.height *= view.height;
            rect.y       = view.y + rect.y * view.height;
            if (Application.isEditor)
            {
                // The Game window's aspect ratio may not match the fake device parameters.
                float realAspect       = (float)screenWidth / screenHeight;
                float fakeAspect       = Cardboard.SDK.Profile.screen.width / Cardboard.SDK.Profile.screen.height;
                float aspectComparison = fakeAspect / realAspect;
                if (aspectComparison < 1)
                {
                    rect.width *= aspectComparison;
                    rect.x     *= aspectComparison;
                    rect.x     += (1 - aspectComparison) / 2;
                }
                else
                {
                    rect.height /= aspectComparison;
                }
            }
            camera.rect = rect;
        }

        // Use the "fast" or "slow" method.  Fast means the camera draws right into one half of
        // the stereo screen.  Slow means it draws first to a side buffer, and then the buffer
        // is written to the screen. The slow method is provided because a lot of Image Effects
        // don't work if you draw to only part of the window.
        if (controller.directRender)
        {
            // Redirect to our stereo screen.
            camera.targetTexture = stereoScreen;
            // Draw!
            camera.Render();
        }
        else
        {
            // Save the viewport rectangle and reset to "full screen".
            Rect pixRect = camera.pixelRect;
            camera.rect = new Rect(0, 0, 1, 1);
            // Redirect to a temporary texture.  The defaults are supposedly Android-friendly.
            int depth = stereoScreen ? stereoScreen.depth : 16;
            RenderTextureFormat format = stereoScreen ? stereoScreen.format : RenderTextureFormat.RGB565;
            camera.targetTexture = RenderTexture.GetTemporary((int)pixRect.width, (int)pixRect.height,
                                                              depth, format);
            // Draw!
            camera.Render();
            // Blit the temp texture to the stereo screen.
            RenderTexture oldTarget = RenderTexture.active;
            RenderTexture.active = stereoScreen;
            GL.PushMatrix();
            GL.LoadPixelMatrix(0, screenWidth, screenHeight, 0);
            // Camera rects are in screen coordinates (bottom left is origin), but DrawTexture takes a
            // rect in GUI coordinates (top left is origin).
            Rect blitRect = pixRect;
            blitRect.y = screenHeight - pixRect.height - pixRect.y;
            // Blit!
            Graphics.DrawTexture(blitRect, camera.targetTexture);
            // Clean up.
            GL.PopMatrix();
            RenderTexture.active = oldTarget;
            RenderTexture.ReleaseTemporary(camera.targetTexture);
        }
        camera.targetTexture = null;
    }
Esempio n. 5
0
    // Helper to copy camera settings from the controller's mono camera.
    // Used in OnPreCull and 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 OnPreCull().
    public void CopyCameraAndMakeSideBySide(StereoController controller,
                                            float parx = 0, float pary = 0)
    {
        var camera = GetComponent <Camera>();

        // Sync the camera properties.
        camera.CopyFrom(controller.GetComponent <Camera>());
        camera.cullingMask ^= toggleCullingMask.value;

        // Reset transform, which was clobbered by the CopyFrom() call.
        // Since we are a child of the mono camera, we inherit its
        // transform already.
        // Use nominal IPD for the editor.  During play, OnPreCull() will
        // compute a real value.
        float ipd = CardboardProfile.Default.device.lenses.separation * controller.stereoMultiplier;

        transform.localPosition = (eye == Cardboard.Eye.Left ? -ipd / 2 : ipd / 2) * Vector3.right;
        transform.localRotation = Quaternion.identity;
        transform.localScale    = Vector3.one;

        // 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 = camera.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, Mathf.Clamp01(controller.stereoPaddingX));
        center.y    = Mathf.Lerp(center.y, 0.5f, Mathf.Clamp01(controller.stereoPaddingY));
        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 == Cardboard.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(controller.screenParallax);

        if (controller.GetComponent <Camera>().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;
        }

        camera.rect = rect;
    }
Esempio n. 6
0
    /// 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(StereoController controller,
                                            float parx = 0, float pary = 0)
    {
#if UNITY_EDITOR
        // Member variable 'camera' not always initialized when this method called in Editor.
        // So, we'll just make a local of the same name.  Same for controller's camera.
        var camera     = GetComponent <Camera>();
        var monoCamera = controller.GetComponent <Camera>();
#endif

        float   ipd           = CardboardProfile.Default.device.lenses.separation * controller.stereoMultiplier;
        Vector3 localPosition = Application.isPlaying ?
                                transform.localPosition : (eye == Cardboard.Eye.Left ? -ipd / 2 : ipd / 2) * Vector3.right;;

        // Sync the camera properties.
        camera.CopyFrom(monoCamera);
        camera.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.
        camera.depth = monoCamera.depth;

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

        // 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 = camera.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, Mathf.Clamp01(controller.stereoPaddingX));
        center.y    = Mathf.Lerp(center.y, 0.5f, Mathf.Clamp01(controller.stereoPaddingY));
        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 == Cardboard.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(controller.screenParallax);
        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;
        }

        camera.rect = rect;
    }
Esempio n. 7
0
    public void Render()
    {
        // Shouldn't happen because of the check in Start(), but just in case...
        if (controller == null)
        {
            return;
        }

        var       camera     = GetComponent <Camera>();
        var       monoCamera = controller.GetComponent <Camera>();
        Matrix4x4 proj       = Cardboard.SDK.Projection(eye);

        CopyCameraAndMakeSideBySide(controller, proj[0, 2], proj[1, 2]);

        // Calculate stereo adjustments based on the center of interest.
        float ipdScale;
        float eyeOffset;

        controller.ComputeStereoAdjustment(proj[1, 1], transform.lossyScale.z,
                                           out ipdScale, out eyeOffset);

        // Set up the eye's view transform.
        transform.localPosition = ipdScale * Cardboard.SDK.EyeOffset(eye) +
                                  eyeOffset * Vector3.forward;

        // Set up the eye's projection.

        // Adjust for non-fullscreen camera.  Cardboard SDK assumes fullscreen,
        // so the aspect ratio might not match.
        proj[0, 0] *= camera.rect.height / camera.rect.width / 2;

        // Adjust for IPD scale.  This changes the vergence of the two frustums.
        Vector2 dir = transform.localPosition; // ignore Z

        dir         = dir.normalized * ipdScale;
        proj[0, 2] *= Mathf.Abs(dir.x);
        proj[1, 2] *= Mathf.Abs(dir.y);

        // Cardboard had to pass "nominal" values of near/far to the SDK, which
        // we fix here to match our mono camera's specific values.
        float near = monoCamera.nearClipPlane;
        float far  = monoCamera.farClipPlane;

        proj[2, 2] = (near + far) / (near - far);
        proj[2, 3] = 2 * near * far / (near - far);

        camera.projectionMatrix = proj;

        if (Application.isEditor)
        {
            // So you can see the approximate frustum in the Scene view when the camera is selected.
            camera.fieldOfView = 2 * Mathf.Atan(1 / proj[1, 1]) * Mathf.Rad2Deg;
        }

        RenderTexture stereoScreen = controller.StereoScreen;

        // Use the "fast" or "slow" method.  Fast means the camera draws right into one half of
        // the stereo screen.  Slow means it draws first to a side buffer, and then the buffer
        // is written to the screen. The slow method is provided because a lot of Image Effects
        // don't work if you draw to only part of the window.
        if (controller.directRender)
        {
            // Redirect to our stereo screen.
            camera.targetTexture = stereoScreen;
            // Draw!
            camera.Render();
        }
        else
        {
            // Save the viewport rectangle and reset to "full screen".
            Rect pixRect = camera.pixelRect;
            camera.rect = new Rect(0, 0, 1, 1);
            // Redirect to a temporary texture.  The defaults are supposedly Android-friendly.
            int depth = stereoScreen ? stereoScreen.depth : 16;
            RenderTextureFormat format = stereoScreen ? stereoScreen.format : RenderTextureFormat.RGB565;
            camera.targetTexture = RenderTexture.GetTemporary((int)pixRect.width, (int)pixRect.height,
                                                              depth, format);
            // Draw!
            camera.Render();
            // Blit the temp texture to the stereo screen.
            RenderTexture oldTarget = RenderTexture.active;
            RenderTexture.active = stereoScreen;
            GL.PushMatrix();
            GL.LoadPixelMatrix(0, stereoScreen ? stereoScreen.width : Screen.width,
                               stereoScreen ? stereoScreen.height : Screen.height, 0);
            Graphics.DrawTexture(pixRect, camera.targetTexture);
            // Clean up.
            GL.PopMatrix();
            RenderTexture.active = oldTarget;
            RenderTexture.ReleaseTemporary(camera.targetTexture);
            camera.targetTexture = null;
        }
    }
Esempio n. 8
0
    private void Setup()
    {
        // Shouldn't happen because of the check in Start(), but just in case...
        if (controller == null)
        {
            return;
        }

        var       monoCamera = controller.GetComponent <Camera>();
        Matrix4x4 proj       = Cardboard.SDK.Projection(eye);

        CopyCameraAndMakeSideBySide(controller, proj[0, 2], proj[1, 2]);

        // Zoom the stereo cameras if requested.
        float lerp = Mathf.Clamp01(controller.matchByZoom) * Mathf.Clamp01(controller.matchMonoFOV);
        // Lerping the reciprocal of proj(1,1) so zooming is linear in the frustum width, not the depth.
        float monoProj11 = monoCamera.projectionMatrix[1, 1];
        float zoom       = 1 / Mathf.Lerp(1 / proj[1, 1], 1 / monoProj11, lerp) / proj[1, 1];

        proj[0, 0] *= zoom;
        proj[1, 1] *= zoom;

        // Calculate stereo adjustments based on the center of interest.
        float ipdScale;
        float eyeOffset;

        controller.ComputeStereoAdjustment(proj[1, 1], transform.lossyScale.z,
                                           out ipdScale, out eyeOffset);

        // Set up the eye's view transform.
        transform.localPosition = ipdScale * Cardboard.SDK.EyePose(eye).Position +
                                  eyeOffset * Vector3.forward;

        // Set up the eye's projection.

        Vuforia.VuforiaBehaviour.Instance.ApplyCorrectedProjectionMatrix(proj, eye == Cardboard.Eye.Left);

        float near = monoCamera.nearClipPlane;
        float far  = monoCamera.farClipPlane;

        FixProjection(ref proj, near, far, ipdScale);
        camera.projectionMatrix = proj;



        if (Application.isEditor)
        {
            // So you can see the approximate frustum in the Scene view when the camera is selected.
            camera.fieldOfView = 2 * Mathf.Atan(1 / proj[1, 1]) * Mathf.Rad2Deg;
        }

        // Set up variables for an image effect that will do distortion correction, e.g. the
        // RadialDistortionEffect.  Note that native distortion correction should take precedence
        // over an image effect, so if that is active then we don't need to compute these variables.
        // (Exception: we're in the editor, so we use the image effect to preview the distortion
        // correction, because native distortion correction only works on the phone.)
        if (Cardboard.SDK.UseDistortionEffect)
        {
            Matrix4x4 realProj = Cardboard.SDK.Projection(eye, Cardboard.Distortion.Undistorted);
            FixProjection(ref realProj, near, far, ipdScale);
            // Parts of the projection matrices that we need to convert texture coordinates between
            // distorted and undistorted frustums.  Include the transform between texture space [0..1]
            // and NDC [-1..1] (that's what the -1 and the /2 are for).  Also note that the zoom
            // factor is removed, because that will interfere with the distortion calculation.
            Vector4 projvec = new Vector4(proj[0, 0] / zoom, proj[1, 1] / zoom,
                                          proj[0, 2] - 1, proj[1, 2] - 1) / 2;
            Vector4 unprojvec = new Vector4(realProj[0, 0], realProj[1, 1],
                                            realProj[0, 2] - 1, realProj[1, 2] - 1) / 2;
            Shader.SetGlobalVector("_Projection", projvec);
            Shader.SetGlobalVector("_Unprojection", unprojvec);
            CardboardProfile p = Cardboard.SDK.Profile;
            Shader.SetGlobalVector("_Undistortion",
                                   new Vector4(p.device.inverse.k1, p.device.inverse.k2));
            Shader.SetGlobalVector("_Distortion",
                                   new Vector4(p.device.distortion.k1, p.device.distortion.k2));
        }

        if (controller.StereoScreen == null)
        {
            Rect rect = camera.rect;
            if (!Cardboard.SDK.DistortionCorrection ||
                Cardboard.SDK.UseDistortionEffect)
            {
                // We are rendering straight to the screen.  Use the reported rect that is visible
                // through the device's lenses.
                Rect view = Cardboard.SDK.Viewport(eye);
                if (eye == Cardboard.Eye.Right)
                {
                    rect.x -= 0.5f;
                }
                rect.width  *= 2 * view.width;
                rect.x       = view.x + 2 * rect.x * view.width;
                rect.height *= view.height;
                rect.y       = view.y + rect.y * view.height;
            }
            if (Application.isEditor)
            {
                // The Game window's aspect ratio may not match the fake device parameters.
                float realAspect       = (float)Screen.width / Screen.height;
                float fakeAspect       = Cardboard.SDK.Profile.screen.width / Cardboard.SDK.Profile.screen.height;
                float aspectComparison = fakeAspect / realAspect;
                if (aspectComparison < 1)
                {
                    rect.width *= aspectComparison;
                    rect.x     *= aspectComparison;
                    rect.x     += (1 - aspectComparison) / 2;
                }
                else
                {
                    rect.height /= aspectComparison;
                }
            }
            camera.rect = rect;
        }
    }
Esempio n. 9
0
    /// 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(StereoController 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 = GvrProfile.Default.viewer.lenses.separation;
        //Vector3 localPosition = Application.isPlaying ?
        //    transform.localPosition : (eye == GvrViewer.Eye.Left ? -ipd/2 : ipd/2) * Vector3.right;
        Vector3 localPosition = (eye == GvrViewer.Eye.Left ? -ipd / 2 : ipd / 2) * Vector3.right;
        Svr.SvrLog.Log("ipd:" + ipd.ToString("F4") + " localPosition:" + localPosition.ToString("F4"));

        // Sync the camera properties.
        cam.CopyFrom(monoCamera);
        cam.cullingMask ^= toggleCullingMask.value;
        if (Svr.SvrSetting.IsVR9Device)
        {
            cam.fieldOfView = GvrProfile.Default.viewer.FOV;
            //cam.clearFlags = CameraClearFlags.Depth;
            Svr.SvrLog.Log("fieldOfView:" + cam.fieldOfView);
        }

        // 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 = monoCamera.depth;

        // 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);
            customSkybox = null;
        }

        // 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, Mathf.Clamp01(controller.stereoPaddingX));
        center.y    = Mathf.Lerp(center.y, 0.5f, Mathf.Clamp01(controller.stereoPaddingY));
        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 == GvrViewer.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(controller.screenParallax);
        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;
        }

        cam.rect = rect;
    }