Search Unity

Question Right eye projection matrix is wrong in Unity OVR (left eye projection matrix is correct)

Discussion in 'VR' started by Adgeinator, Apr 21, 2021.

  1. Adgeinator

    Adgeinator

    Joined:
    Nov 6, 2020
    Posts:
    9
    Has anyone figured out how to get the correct left and right eye projection matrixes in Unity? My left eye looks exactly correct, however, my right eye projection matrix makes it so that objects warp when moving my HMD. I am using the Oculus Quest 2 with the OVR dev kit.

    I am trying to make a portal effect, and when I close my left eye it looks perfect. Here is a video, but it doesn't demonstrate the problem (as it only occurs in the left eye).
    https://drive.google.com/file/d/1S6bfHcxtZ0AB0LnhE8ayGeIEzUGtISwi/view?usp=sharing

    Here is the code that I am using to get the projection matrixes for each eye:


    Code (CSharp):
    1. // left eye projection matrix
    2.     OVRManager.instance.gameObject.GetComponent<OVRCameraRig>().leftEyeCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left)
    3.     // right eye projection matrix
    4.     OVRManager.instance.gameObject.GetComponent<OVRCameraRig>().rightEyeCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right)
    Here is what I am doing to render the eyes to textures (it is a modified version of the HTC Stereo Rendering package)
    Code (CSharp):
    1.  
    2.     private void RenderToTwoStereoTextures(VRRenderEventDetector detector)
    3.                 {
    4.                     // get eye poses
    5.                     var leftEyeOffset = StereoRenderManager.Instance.paramFactory.GetEyeSeperation(0);
    6.                     var leftEyeRotation = StereoRenderManager.Instance.paramFactory.GetEyeLocalRotation(0);
    7.        
    8.                     var rightEyeOffset = StereoRenderManager.Instance.paramFactory.GetEyeSeperation(1);
    9.                     var rightEyeRotation = StereoRenderManager.Instance.paramFactory.GetEyeLocalRotation(1);
    10.        
    11.                     // render stereo textures
    12.                     RenderEye(
    13.                         leftEyeOffset, leftEyeRotation,
    14.                         leftProjMatrix, detector.unityCamera.worldToCameraMatrix,
    15.                         leftEyeTexture, "_LeftEyeTexture");
    16.        
    17.                     var rightEyeWorldToCameraMatrix = detector.unityCamera.worldToCameraMatrix;
    18.                     rightEyeWorldToCameraMatrix.m03 -= 2.0f * Mathf.Abs(leftEyeOffset.x);
    19.        
    20.                     RenderEye(
    21.                         rightEyeOffset, rightEyeRotation,
    22.                         rightProjMatrix, rightEyeWorldToCameraMatrix,
    23.                         rightEyeTexture, "_RightEyeTexture");
    24.                 }
    25.    
    26.             private void RenderToTwoStereoTextures(VRRenderEventDetector detector)
    27.             {
    28.                 // get eye poses
    29.                 var leftEyeOffset = StereoRenderManager.Instance.paramFactory.GetEyeSeperation(0);
    30.                 var leftEyeRotation = StereoRenderManager.Instance.paramFactory.GetEyeLocalRotation(0);
    31.    
    32.                 var rightEyeOffset = StereoRenderManager.Instance.paramFactory.GetEyeSeperation(1);
    33.                 var rightEyeRotation = StereoRenderManager.Instance.paramFactory.GetEyeLocalRotation(1);
    34.    
    35.                 // render stereo textures
    36.                 RenderEye(
    37.                     leftEyeOffset, leftEyeRotation,
    38.                     leftProjMatrix, detector.unityCamera.worldToCameraMatrix,
    39.                     leftEyeTexture, "_LeftEyeTexture");
    40.    
    41.                 var rightEyeWorldToCameraMatrix = detector.unityCamera.worldToCameraMatrix;
    42.                 rightEyeWorldToCameraMatrix.m03 -= 2.0f * Mathf.Abs(leftEyeOffset.x);
    43.    
    44.                 RenderEye(
    45.                     rightEyeOffset, rightEyeRotation,
    46.                     rightProjMatrix, rightEyeWorldToCameraMatrix,
    47.                     rightEyeTexture, "_RightEyeTexture");
    48.             }
    49.    
    50.    
    51.             private Vector4 GetCameraSpacePlane(Camera cam, Vector3 pt, Vector3 normal)
    52.             {
    53.                 Matrix4x4 m = cam.worldToCameraMatrix;
    54.                 Vector3 camSpacePt = m.MultiplyPoint(pt);
    55.                 Vector3 camSpaceNormal = m.MultiplyVector(normal).normalized;
    56.                 return new Vector4(
    57.                     camSpaceNormal.x,
    58.                     camSpaceNormal.y,
    59.                     camSpaceNormal.z,
    60.                     -Vector3.Dot(camSpacePt, camSpaceNormal));
    61.             }
    62.    
    63.             private Vector4 GetObliqueNearClipPlane()
    64.             {
    65.                 var clipPlaneCameraSpace = Vector4.zero;
    66.                 if (!isMirror)
    67.                 {
    68.                     clipPlaneCameraSpace = GetCameraSpacePlane(stereoCameraEye, anchorPos, anchorForward);
    69.                 }
    70.                 else
    71.                 {
    72.                     // get reflection plane -- assume +y as normal
    73.                     float d = -Vector3.Dot(canvasOriginUp, canvasOriginPos) - reflectionOffset;
    74.                     Vector4 reflectionPlane = new Vector4(canvasOriginUp.x, canvasOriginUp.y, canvasOriginUp.z, d);
    75.    
    76.                     clipPlaneCameraSpace = GetCameraSpacePlane(stereoCameraEye, canvasOriginPos, reflectionPlane);
    77.                 }
    78.    
    79.                 return clipPlaneCameraSpace;
    80.             }
    81.    
    82.             private Rect GetScissorRect(Matrix4x4 mat)
    83.             {
    84.                 var renderer = GetComponent<Renderer>();
    85.                 Vector3 cen = renderer.bounds.center;
    86.                 Vector3 ext = renderer.bounds.extents;
    87.                 Vector3[] extentPoints = new Vector3[8]
    88.                 {
    89.                      WorldPointToViewport(mat, new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z-ext.z)),
    90.                      WorldPointToViewport(mat, new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z-ext.z)),
    91.                      WorldPointToViewport(mat, new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z+ext.z)),
    92.                      WorldPointToViewport(mat, new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z+ext.z)),
    93.                      WorldPointToViewport(mat, new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z-ext.z)),
    94.                      WorldPointToViewport(mat, new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z-ext.z)),
    95.                      WorldPointToViewport(mat, new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z+ext.z)),
    96.                      WorldPointToViewport(mat, new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z+ext.z))
    97.                 };
    98.    
    99.                 bool invalidFlag = false;
    100.                 Vector2 min = extentPoints[0];
    101.                 Vector2 max = extentPoints[0];
    102.                 foreach (Vector3 v in extentPoints)
    103.                 {
    104.                     // if v.z < 0 means this projection is unreliable
    105.                     if (v.z < 0)
    106.                     {
    107.                         invalidFlag = true;
    108.                         break;
    109.                     }
    110.    
    111.                     min = Vector2.Min(min, v);
    112.                     max = Vector2.Max(max, v);
    113.                 }
    114.    
    115.                 if (invalidFlag)
    116.                 {
    117.                     return fullViewport;
    118.                 }
    119.                 else
    120.                 {
    121.                     min = Vector2.Max(min, Vector2.zero);
    122.                     max = Vector2.Min(max, Vector2.one);
    123.                     return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
    124.                 }
    125.             }
    126.    
    127.             private Matrix4x4 GetScissorMatrix(Rect rect)
    128.             {
    129.                 Matrix4x4 m2 = Matrix4x4.TRS(
    130.                     new Vector3((1 / rect.width - 1), (1 / rect.height - 1), 0),
    131.                     Quaternion.identity,
    132.                     new Vector3(1 / rect.width, 1 / rect.height, 1));
    133.    
    134.                 Matrix4x4 m3 = Matrix4x4.TRS(
    135.                     new Vector3(-rect.x * 2 / rect.width, -rect.y * 2 / rect.height, 0),
    136.                     Quaternion.identity,
    137.                     Vector3.one);
    138.    
    139.                 return m3 * m2;
    140.             }
    141.    
    142.             private Vector3 WorldPointToViewport(Matrix4x4 mat, Vector3 point)
    143.             {
    144.                 Vector3 result;
    145.                 result.x = mat.m00 * point.x + mat.m01 * point.y + mat.m02 * point.z + mat.m03;
    146.                 result.y = mat.m10 * point.x + mat.m11 * point.y + mat.m12 * point.z + mat.m13;
    147.                 result.z = mat.m20 * point.x + mat.m21 * point.y + mat.m22 * point.z + mat.m23;
    148.    
    149.                 float a = mat.m30 * point.x + mat.m31 * point.y + mat.m32 * point.z + mat.m33;
    150.                 a = 1.0f / a;
    151.                 result.x *= a;
    152.                 result.y *= a;
    153.                 result.z = a;
    154.    
    155.                 point = result;
    156.                 point.x = (point.x * 0.5f + 0.5f);
    157.                 point.y = (point.y * 0.5f + 0.5f);
    158.    
    159.                 return point;
    160.             }
    161.      
     
  2. Arclite83

    Arclite83

    Joined:
    Feb 6, 2018
    Posts:
    4
    I've been tinkering with this problem in various forms since October. Here's what I can say:

    I have two implementations - the first, based a bit on Sebastian Lague's code, has perfect imagery (no need for the offset, I get a correct projection matrix value for m03), but travel is not seamless - I haven't solved the "per eye" aspect of it with transforms.

    The second has this exact issue you describe, and is based on work with Pocket Portals (a store asset). That one has perfect "step through" camera (because it's all layers), but I had to set this offset.

    I'm only planning to release on Quest 2 for now for my project, so... it's a hack I'm living with.

    There's a CHANCE this is new behavior from a quest update, because I really don't recall seeing m03 change before, but that's 100% an unfounded feeling atm, haha.
     
  3. scott_05

    scott_05

    Joined:
    Nov 9, 2020
    Posts:
    1
    Hi, im having similar issues with a planar reflection plugin I am using. Works great in the left eye, but the right eye is all warped and distorted. Cant for the life of me figure it out.