oculus1

diff libovr/Src/Util/Util_Render_Stereo.cpp @ 1:e2f9e4603129

added LibOVR and started a simple vr wrapper.
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 14 Sep 2013 16:14:59 +0300
parents
children b069a5c27388
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libovr/Src/Util/Util_Render_Stereo.cpp	Sat Sep 14 16:14:59 2013 +0300
     1.3 @@ -0,0 +1,1 @@
     1.4 +/************************************************************************************
     1.5 
     1.6 Filename    :   Util_Render_Stereo.cpp
     1.7 Content     :   Stereo rendering configuration implementation
     1.8 Created     :   October 22, 2012
     1.9 Authors     :   Michael Antonov, Andrew Reisse
    1.10 
    1.11 Copyright   :   Copyright 2012 Oculus, Inc. All Rights reserved.
    1.12 
    1.13 Use of this software is subject to the terms of the Oculus Inc license
    1.14 agreement provided at the time of installation or download, or which
    1.15 otherwise accompanies this software in either electronic or hard copy form.
    1.16 
    1.17 *************************************************************************************/
    1.18 
    1.19 #include "Util_Render_Stereo.h"
    1.20 
    1.21 namespace OVR { namespace Util { namespace Render {
    1.22 
    1.23 
    1.24 //-----------------------------------------------------------------------------------
    1.25 
    1.26 // DistortionFnInverse computes the inverse of the distortion function on an argument.
    1.27 float DistortionConfig::DistortionFnInverse(float r)
    1.28 {    
    1.29     OVR_ASSERT((r <= 10.0f));
    1.30 
    1.31     float s, d;
    1.32     float delta = r * 0.25f;
    1.33 
    1.34     s = r * 0.5f;
    1.35     d = fabs(r - DistortionFn(s));
    1.36 
    1.37     for (int i = 0; i < 20; i++)
    1.38     {
    1.39         float sUp   = s + delta;
    1.40         float sDown = s - delta;
    1.41         float dUp   = fabs(r - DistortionFn(sUp));
    1.42         float dDown = fabs(r - DistortionFn(sDown));
    1.43 
    1.44         if (dUp < d)
    1.45         {
    1.46             s = sUp;
    1.47             d = dUp;
    1.48         }
    1.49         else if (dDown < d)
    1.50         {
    1.51             s = sDown;
    1.52             d = dDown;
    1.53         }
    1.54         else
    1.55         {
    1.56             delta *= 0.5f;
    1.57         }
    1.58     }
    1.59 
    1.60     return s;
    1.61 }
    1.62 
    1.63 
    1.64 //-----------------------------------------------------------------------------------
    1.65 // **** StereoConfig Implementation
    1.66 
    1.67 StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp)
    1.68     : Mode(mode),
    1.69       InterpupillaryDistance(0.064f), AspectMultiplier(1.0f),
    1.70       FullView(vp), DirtyFlag(true), IPDOverride(false),
    1.71       YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0),
    1.72       OrthoPixelOffset(0)
    1.73 {
    1.74     // And default distortion for it.
    1.75     Distortion.SetCoefficients(1.0f, 0.22f, 0.24f);
    1.76     Distortion.Scale = 1.0f; // Will be computed later.
    1.77 
    1.78     // Fit left of the image.
    1.79     DistortionFitX = -1.0f;
    1.80     DistortionFitY = 0.0f;
    1.81 
    1.82     // Initialize "fake" default HMD values for testing without HMD plugged in.
    1.83     // These default values match those returned by the HMD.
    1.84     HMD.HResolution            = 1280;
    1.85     HMD.VResolution            = 800;
    1.86     HMD.HScreenSize            = 0.14976f;
    1.87     HMD.VScreenSize            = HMD.HScreenSize / (1280.0f / 800.0f);
    1.88     HMD.InterpupillaryDistance = InterpupillaryDistance;
    1.89     HMD.LensSeparationDistance = 0.0635f;
    1.90     HMD.EyeToScreenDistance    = 0.041f;
    1.91     HMD.DistortionK[0]         = Distortion.K[0];
    1.92     HMD.DistortionK[1]         = Distortion.K[1];
    1.93     HMD.DistortionK[2]         = Distortion.K[2];
    1.94     HMD.DistortionK[3]         = 0;
    1.95 
    1.96     Set2DAreaFov(DegreeToRad(85.0f));
    1.97 }
    1.98 
    1.99 void StereoConfig::SetFullViewport(const Viewport& vp)
   1.100 {
   1.101     if (vp != FullView)
   1.102     { 
   1.103         FullView = vp;
   1.104         DirtyFlag = true;
   1.105     }
   1.106 }
   1.107 
   1.108 void StereoConfig::SetHMDInfo(const HMDInfo& hmd)
   1.109 {
   1.110     HMD = hmd;
   1.111     Distortion.K[0] = hmd.DistortionK[0];
   1.112     Distortion.K[1] = hmd.DistortionK[1];
   1.113     Distortion.K[2] = hmd.DistortionK[2];
   1.114     Distortion.K[3] = hmd.DistortionK[3];
   1.115 
   1.116     Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1],
   1.117                                       hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]);
   1.118 
   1.119     if (!IPDOverride)
   1.120         InterpupillaryDistance = HMD.InterpupillaryDistance;
   1.121 
   1.122     DirtyFlag = true;
   1.123 }
   1.124 
   1.125 void StereoConfig::SetDistortionFitPointVP(float x, float y)
   1.126 {
   1.127     DistortionFitX = x;
   1.128     DistortionFitY = y;
   1.129     DirtyFlag = true;
   1.130 }
   1.131 
   1.132 void StereoConfig::SetDistortionFitPointPixels(float x, float y)
   1.133 {
   1.134     DistortionFitX = (4 * x / float(FullView.w)) - 1.0f;
   1.135     DistortionFitY = (2 * y / float(FullView.h)) - 1.0f;
   1.136     DirtyFlag = true;
   1.137 }
   1.138 
   1.139 void StereoConfig::Set2DAreaFov(float fovRadians)
   1.140 {
   1.141     Area2DFov = fovRadians;
   1.142     DirtyFlag = true;
   1.143 }
   1.144 
   1.145 
   1.146 const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye)
   1.147 {
   1.148     static const UByte eyeParamIndices[3] = { 0, 0, 1 };
   1.149 
   1.150     updateIfDirty();
   1.151     OVR_ASSERT(eye < sizeof(eyeParamIndices));
   1.152     return EyeRenderParams[eyeParamIndices[eye]];
   1.153 }
   1.154 
   1.155 
   1.156 void StereoConfig::updateComputedState()
   1.157 {
   1.158     // Need to compute all of the following:
   1.159     //   - Aspect Ratio
   1.160     //   - FOV
   1.161     //   - Projection offsets for 3D
   1.162     //   - Distortion XCenterOffset
   1.163     //   - Update 2D
   1.164     //   - Initialize EyeRenderParams
   1.165 
   1.166     // Compute aspect ratio. Stereo mode cuts width in half.
   1.167     Aspect = float(FullView.w) / float(FullView.h);
   1.168     Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f;
   1.169     Aspect *= AspectMultiplier; 
   1.170 
   1.171     updateDistortionOffsetAndScale();
   1.172 
   1.173     // Compute Vertical FOV based on distance, distortion, etc.
   1.174     // Distance from vertical center to render vertical edge perceived through the lens.
   1.175     // This will be larger then normal screen size due to magnification & distortion.
   1.176     //
   1.177     // This percievedHalfRTDistance equation should hold as long as the render target
   1.178     // and display have the same aspect ratios. What we'd like to know is where the edge
   1.179     // of the render target will on the perceived screen surface. With NO LENS,
   1.180     // the answer would be:
   1.181     //
   1.182     //  halfRTDistance = (VScreenSize / 2) * aspect *
   1.183     //                   DistortionFn_Inverse( DistortionScale / aspect )
   1.184     //
   1.185     // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios
   1.186     // cancel out, so we get:
   1.187     //
   1.188     //  halfRTDistance = (VScreenSize / 2) * DistortionScale
   1.189     //
   1.190     if (Mode == Stereo_None)
   1.191     {
   1.192         YFov = DegreeToRad(80.0f);
   1.193     }
   1.194     else
   1.195     {
   1.196         float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale;    
   1.197         YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance);
   1.198     }
   1.199     
   1.200     updateProjectionOffset();
   1.201     update2D();
   1.202     updateEyeParams();
   1.203 
   1.204     DirtyFlag = false;
   1.205 }
   1.206 
   1.207 void StereoConfig::updateDistortionOffsetAndScale()
   1.208 {
   1.209     // Distortion center shift is stored separately, since it isn't affected
   1.210     // by the eye distance.
   1.211     float lensOffset        = HMD.LensSeparationDistance * 0.5f;
   1.212     float lensShift         = HMD.HScreenSize * 0.25f - lensOffset;
   1.213     float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize;
   1.214     Distortion.XCenterOffset= lensViewportShift;
   1.215 
   1.216     // Compute distortion scale from DistortionFitX & DistortionFitY.
   1.217     // Fit value of 0.0 means "no fit".
   1.218     if ((fabs(DistortionFitX) < 0.0001f) &&  (fabs(DistortionFitY) < 0.0001f))
   1.219     {
   1.220         Distortion.Scale = 1.0f;
   1.221     }
   1.222     else
   1.223     {
   1.224         // Convert fit value to distortion-centered coordinates before fit radius
   1.225         // calculation.
   1.226         float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h);
   1.227         float dx           = DistortionFitX - Distortion.XCenterOffset;
   1.228         float dy           = DistortionFitY / stereoAspect;
   1.229         float fitRadius    = sqrt(dx * dx + dy * dy);
   1.230         Distortion.Scale   = Distortion.DistortionFn(fitRadius)/fitRadius;
   1.231     }
   1.232 }
   1.233 
   1.234 void StereoConfig::updateProjectionOffset()
   1.235 {
   1.236     // Post-projection viewport coordinates range from (-1.0, 1.0), with the
   1.237     // center of the left viewport falling at (1/4) of horizontal screen size.
   1.238     // We need to shift this projection center to match with the lens center;
   1.239     // note that we don't use the IPD here due to collimated light property of the lens.
   1.240     // We compute this shift in physical units (meters) to
   1.241     // correct for different screen sizes and then rescale to viewport coordinates.    
   1.242     float viewCenter         = HMD.HScreenSize * 0.25f;
   1.243     float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f;
   1.244     ProjectionCenterOffset   = 4.0f * eyeProjectionShift / HMD.HScreenSize;
   1.245 }
   1.246 
   1.247 void StereoConfig::update2D()
   1.248 {
   1.249     // Orthographic projection fakes a screen at a distance of 0.8m from the
   1.250     // eye, where hmd screen projection surface is at 0.05m distance.
   1.251     // This introduces an extra off-center pixel projection shift based on eye distance.
   1.252     // This offCenterShift is the pixel offset of the other camera's center
   1.253     // in your reference camera based on surface distance.    
   1.254     float metersToPixels          = (HMD.HResolution / HMD.HScreenSize);
   1.255     float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance;
   1.256     float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance;
   1.257     float offCenterShiftPixels    = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels;
   1.258     float leftPixelCenter         = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f;
   1.259     float rightPixelCenter        = lensDistanceScreenPixels * 0.5f;
   1.260     float pixelDifference         = leftPixelCenter - rightPixelCenter;
   1.261     
   1.262     // This computes the number of pixels that fit within specified 2D FOV (assuming
   1.263     // distortion scaling will be done).
   1.264     float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance;
   1.265     float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale;
   1.266     FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize;
   1.267     
   1.268     // Create orthographic matrix.   
   1.269     Matrix4f& m      = OrthoCenter;
   1.270     m.SetIdentity();
   1.271     m.M[0][0] = FovPixels / (FullView.w * 0.5f);
   1.272     m.M[1][1] = -FovPixels / FullView.h;
   1.273     m.M[0][3] = 0;
   1.274     m.M[1][3] = 0;
   1.275     m.M[2][2] = 0;
   1.276 
   1.277     float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f;
   1.278     OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels;
   1.279 }
   1.280 
   1.281 void StereoConfig::updateEyeParams()
   1.282 {
   1.283     // Projection matrix for the center eye, which the left/right matrices are based on.
   1.284     Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 2000.0f);
   1.285    
   1.286     switch(Mode)
   1.287     {
   1.288     case Stereo_None:
   1.289         {
   1.290             EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter);
   1.291         }
   1.292         break;
   1.293 
   1.294     case Stereo_LeftRight_Multipass:
   1.295         {
   1.296             Matrix4f projLeft  = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter,
   1.297                      projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter;
   1.298 
   1.299             EyeRenderParams[0].Init(StereoEye_Left,
   1.300                 Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h),
   1.301                          +InterpupillaryDistance * 0.5f,  // World view shift.                       
   1.302                          projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0),
   1.303                          &Distortion);
   1.304             EyeRenderParams[1].Init(StereoEye_Right,
   1.305                 Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h),
   1.306                          -InterpupillaryDistance * 0.5f,                         
   1.307                          projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0),
   1.308                          &Distortion);
   1.309         }
   1.310         break;
   1.311     }
   1.312 
   1.313 }
   1.314 
   1.315 
   1.316 }}}  // OVR::Util::Render
   1.317 
   1.318 \ No newline at end of file