Util_Render_Stereo.cpp
Go to the documentation of this file.
00001 /************************************************************************************
00002 
00003 Filename    :   Util_Render_Stereo.cpp
00004 Content     :   Stereo rendering configuration implementation
00005 Created     :   October 22, 2012
00006 Authors     :   Michael Antonov, Andrew Reisse
00007 
00008 Copyright   :   Copyright 2012 Oculus, Inc. All Rights reserved.
00009 
00010 Use of this software is subject to the terms of the Oculus Inc license
00011 agreement provided at the time of installation or download, or which
00012 otherwise accompanies this software in either electronic or hard copy form.
00013 
00014 *************************************************************************************/
00015 
00016 #include "Util_Render_Stereo.h"
00017 
00018 namespace OVR { namespace Util { namespace Render {
00019 
00020 
00021 //-----------------------------------------------------------------------------------
00022 
00023 // DistortionFnInverse computes the inverse of the distortion function on an argument.
00024 float DistortionConfig::DistortionFnInverse(float r)
00025 {    
00026     OVR_ASSERT((r <= 10.0f));
00027 
00028     float s, d;
00029     float delta = r * 0.25f;
00030 
00031     s = r * 0.5f;
00032     d = fabs(r - DistortionFn(s));
00033 
00034     for (int i = 0; i < 20; i++)
00035     {
00036         float sUp   = s + delta;
00037         float sDown = s - delta;
00038         float dUp   = fabs(r - DistortionFn(sUp));
00039         float dDown = fabs(r - DistortionFn(sDown));
00040 
00041         if (dUp < d)
00042         {
00043             s = sUp;
00044             d = dUp;
00045         }
00046         else if (dDown < d)
00047         {
00048             s = sDown;
00049             d = dDown;
00050         }
00051         else
00052         {
00053             delta *= 0.5f;
00054         }
00055     }
00056 
00057     return s;
00058 }
00059 
00060 
00061 //-----------------------------------------------------------------------------------
00062 // **** StereoConfig Implementation
00063 
00064 StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp)
00065     : Mode(mode),
00066       InterpupillaryDistance(0.064f), AspectMultiplier(1.0f),
00067       FullView(vp), DirtyFlag(true), IPDOverride(false),
00068       YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0),
00069       OrthoPixelOffset(0)
00070 {
00071     // And default distortion for it.
00072     Distortion.SetCoefficients(1.0f, 0.22f, 0.24f);
00073     Distortion.Scale = 1.0f; // Will be computed later.
00074 
00075     // Fit left of the image.
00076     DistortionFitX = -1.0f;
00077     DistortionFitY = 0.0f;
00078 
00079     // Initialize "fake" default HMD values for testing without HMD plugged in.
00080     // These default values match those returned by the HMD.
00081     HMD.HResolution            = 1280;
00082     HMD.VResolution            = 800;
00083     HMD.HScreenSize            = 0.14976f;
00084     HMD.VScreenSize            = HMD.HScreenSize / (1280.0f / 800.0f);
00085     HMD.InterpupillaryDistance = InterpupillaryDistance;
00086     HMD.LensSeparationDistance = 0.0635f;
00087     HMD.EyeToScreenDistance    = 0.041f;
00088     HMD.DistortionK[0]         = Distortion.K[0];
00089     HMD.DistortionK[1]         = Distortion.K[1];
00090     HMD.DistortionK[2]         = Distortion.K[2];
00091     HMD.DistortionK[3]         = 0;
00092 
00093     Set2DAreaFov(DegreeToRad(85.0f));
00094 }
00095 
00096 void StereoConfig::SetFullViewport(const Viewport& vp)
00097 {
00098     if (vp != FullView)
00099     { 
00100         FullView = vp;
00101         DirtyFlag = true;
00102     }
00103 }
00104 
00105 void StereoConfig::SetHMDInfo(const HMDInfo& hmd)
00106 {
00107     HMD = hmd;
00108     Distortion.K[0] = hmd.DistortionK[0];
00109     Distortion.K[1] = hmd.DistortionK[1];
00110     Distortion.K[2] = hmd.DistortionK[2];
00111     Distortion.K[3] = hmd.DistortionK[3];
00112 
00113     Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1],
00114                                       hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]);
00115 
00116     if (!IPDOverride)
00117         InterpupillaryDistance = HMD.InterpupillaryDistance;
00118 
00119     DirtyFlag = true;
00120 }
00121 
00122 void StereoConfig::SetDistortionFitPointVP(float x, float y)
00123 {
00124     DistortionFitX = x;
00125     DistortionFitY = y;
00126     DirtyFlag = true;
00127 }
00128 
00129 void StereoConfig::SetDistortionFitPointPixels(float x, float y)
00130 {
00131     DistortionFitX = (4 * x / float(FullView.w)) - 1.0f;
00132     DistortionFitY = (2 * y / float(FullView.h)) - 1.0f;
00133     DirtyFlag = true;
00134 }
00135 
00136 void StereoConfig::Set2DAreaFov(float fovRadians)
00137 {
00138     Area2DFov = fovRadians;
00139     DirtyFlag = true;
00140 }
00141 
00142 
00143 const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye)
00144 {
00145     static const UByte eyeParamIndices[3] = { 0, 0, 1 };
00146 
00147     updateIfDirty();
00148     OVR_ASSERT(eye < sizeof(eyeParamIndices));
00149     return EyeRenderParams[eyeParamIndices[eye]];
00150 }
00151 
00152 
00153 void StereoConfig::updateComputedState()
00154 {
00155     // Need to compute all of the following:
00156     //   - Aspect Ratio
00157     //   - FOV
00158     //   - Projection offsets for 3D
00159     //   - Distortion XCenterOffset
00160     //   - Update 2D
00161     //   - Initialize EyeRenderParams
00162 
00163     // Compute aspect ratio. Stereo mode cuts width in half.
00164     Aspect = float(FullView.w) / float(FullView.h);
00165     Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f;
00166     Aspect *= AspectMultiplier; 
00167 
00168     updateDistortionOffsetAndScale();
00169 
00170     // Compute Vertical FOV based on distance, distortion, etc.
00171     // Distance from vertical center to render vertical edge perceived through the lens.
00172     // This will be larger then normal screen size due to magnification & distortion.
00173     //
00174     // This percievedHalfRTDistance equation should hold as long as the render target
00175     // and display have the same aspect ratios. What we'd like to know is where the edge
00176     // of the render target will on the perceived screen surface. With NO LENS,
00177     // the answer would be:
00178     //
00179     //  halfRTDistance = (VScreenSize / 2) * aspect *
00180     //                   DistortionFn_Inverse( DistortionScale / aspect )
00181     //
00182     // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios
00183     // cancel out, so we get:
00184     //
00185     //  halfRTDistance = (VScreenSize / 2) * DistortionScale
00186     //
00187     if (Mode == Stereo_None)
00188     {
00189         YFov = DegreeToRad(80.0f);
00190     }
00191     else
00192     {
00193         float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale;    
00194         YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance);
00195     }
00196     
00197     updateProjectionOffset();
00198     update2D();
00199     updateEyeParams();
00200 
00201     DirtyFlag = false;
00202 }
00203 
00204 void StereoConfig::updateDistortionOffsetAndScale()
00205 {
00206     // Distortion center shift is stored separately, since it isn't affected
00207     // by the eye distance.
00208     float lensOffset        = HMD.LensSeparationDistance * 0.5f;
00209     float lensShift         = HMD.HScreenSize * 0.25f - lensOffset;
00210     float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize;
00211     Distortion.XCenterOffset= lensViewportShift;
00212 
00213     // Compute distortion scale from DistortionFitX & DistortionFitY.
00214     // Fit value of 0.0 means "no fit".
00215     if ((fabs(DistortionFitX) < 0.0001f) &&  (fabs(DistortionFitY) < 0.0001f))
00216     {
00217         Distortion.Scale = 1.0f;
00218     }
00219     else
00220     {
00221         // Convert fit value to distortion-centered coordinates before fit radius
00222         // calculation.
00223         float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h);
00224         float dx           = DistortionFitX - Distortion.XCenterOffset;
00225         float dy           = DistortionFitY / stereoAspect;
00226         float fitRadius    = sqrt(dx * dx + dy * dy);
00227         Distortion.Scale   = Distortion.DistortionFn(fitRadius)/fitRadius;
00228     }
00229 }
00230 
00231 void StereoConfig::updateProjectionOffset()
00232 {
00233     // Post-projection viewport coordinates range from (-1.0, 1.0), with the
00234     // center of the left viewport falling at (1/4) of horizontal screen size.
00235     // We need to shift this projection center to match with the lens center;
00236     // note that we don't use the IPD here due to collimated light property of the lens.
00237     // We compute this shift in physical units (meters) to
00238     // correct for different screen sizes and then rescale to viewport coordinates.    
00239     float viewCenter         = HMD.HScreenSize * 0.25f;
00240     float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f;
00241     ProjectionCenterOffset   = 4.0f * eyeProjectionShift / HMD.HScreenSize;
00242 }
00243 
00244 void StereoConfig::update2D()
00245 {
00246     // Orthographic projection fakes a screen at a distance of 0.8m from the
00247     // eye, where hmd screen projection surface is at 0.05m distance.
00248     // This introduces an extra off-center pixel projection shift based on eye distance.
00249     // This offCenterShift is the pixel offset of the other camera's center
00250     // in your reference camera based on surface distance.
00251     float metersToPixels          = (HMD.HResolution / HMD.HScreenSize);
00252     float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance;
00253     float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance;
00254     float offCenterShiftPixels    = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels;
00255     float leftPixelCenter         = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f;
00256     float rightPixelCenter        = lensDistanceScreenPixels * 0.5f;
00257     float pixelDifference         = leftPixelCenter - rightPixelCenter;
00258     
00259     // This computes the number of pixels that fit within specified 2D FOV (assuming
00260     // distortion scaling will be done).
00261     float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance;
00262     float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale;
00263     FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize;
00264     
00265     // Create orthographic matrix.   
00266     Matrix4f& m      = OrthoCenter;
00267     m.SetIdentity();
00268     m.M[0][0] = FovPixels / (FullView.w * 0.5f);
00269     m.M[1][1] = -FovPixels / FullView.h;
00270     m.M[0][3] = 0;
00271     m.M[1][3] = 0;
00272     m.M[2][2] = 0;
00273 
00274     float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f;
00275     OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels;
00276 }
00277 
00278 void StereoConfig::updateEyeParams()
00279 {
00280     // Projection matrix for the center eye, which the left/right matrices are based on.
00281     Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 1000.0f);
00282    
00283     switch(Mode)
00284     {
00285     case Stereo_None:
00286         {
00287             EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter);
00288         }
00289         break;
00290 
00291     case Stereo_LeftRight_Multipass:
00292         {
00293             Matrix4f projLeft  = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter,
00294                      projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter;
00295 
00296             EyeRenderParams[0].Init(StereoEye_Left,
00297                 Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h),
00298                          +InterpupillaryDistance * 0.5f,  // World view shift.                       
00299                          projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0),
00300                          &Distortion);
00301             EyeRenderParams[1].Init(StereoEye_Right,
00302                 Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h),
00303                          -InterpupillaryDistance * 0.5f,                         
00304                          projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0),
00305                          &Distortion);
00306         }
00307         break;
00308     }
00309 
00310 }
00311 
00312 
00313 }}}  // OVR::Util::Render
00314 


oculus_sdk
Author(s):
autogenerated on Mon Oct 6 2014 03:01:19