oculus1
view libovr/Src/Util/Util_Render_Stereo.cpp @ 17:cfe4979ab3eb
ops, minor error in the last commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 21 Sep 2013 07:09:48 +0300 |
parents | e2f9e4603129 |
children |
line source
1 /************************************************************************************
3 Filename : Util_Render_Stereo.cpp
4 Content : Stereo rendering configuration implementation
5 Created : October 22, 2012
6 Authors : Michael Antonov, Andrew Reisse
8 Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
10 Use of this software is subject to the terms of the Oculus Inc license
11 agreement provided at the time of installation or download, or which
12 otherwise accompanies this software in either electronic or hard copy form.
14 *************************************************************************************/
16 #include "Util_Render_Stereo.h"
18 namespace OVR { namespace Util { namespace Render {
21 //-----------------------------------------------------------------------------------
23 // DistortionFnInverse computes the inverse of the distortion function on an argument.
24 float DistortionConfig::DistortionFnInverse(float r)
25 {
26 OVR_ASSERT((r <= 10.0f));
28 float s, d;
29 float delta = r * 0.25f;
31 s = r * 0.5f;
32 d = fabs(r - DistortionFn(s));
34 for (int i = 0; i < 20; i++)
35 {
36 float sUp = s + delta;
37 float sDown = s - delta;
38 float dUp = fabs(r - DistortionFn(sUp));
39 float dDown = fabs(r - DistortionFn(sDown));
41 if (dUp < d)
42 {
43 s = sUp;
44 d = dUp;
45 }
46 else if (dDown < d)
47 {
48 s = sDown;
49 d = dDown;
50 }
51 else
52 {
53 delta *= 0.5f;
54 }
55 }
57 return s;
58 }
61 //-----------------------------------------------------------------------------------
62 // **** StereoConfig Implementation
64 StereoConfig::StereoConfig(StereoMode mode, const Viewport& vp)
65 : Mode(mode),
66 InterpupillaryDistance(0.064f), AspectMultiplier(1.0f),
67 FullView(vp), DirtyFlag(true), IPDOverride(false),
68 YFov(0), Aspect(vp.w / float(vp.h)), ProjectionCenterOffset(0),
69 OrthoPixelOffset(0)
70 {
71 // And default distortion for it.
72 Distortion.SetCoefficients(1.0f, 0.22f, 0.24f);
73 Distortion.Scale = 1.0f; // Will be computed later.
75 // Fit left of the image.
76 DistortionFitX = -1.0f;
77 DistortionFitY = 0.0f;
79 // Initialize "fake" default HMD values for testing without HMD plugged in.
80 // These default values match those returned by the HMD.
81 HMD.HResolution = 1280;
82 HMD.VResolution = 800;
83 HMD.HScreenSize = 0.14976f;
84 HMD.VScreenSize = HMD.HScreenSize / (1280.0f / 800.0f);
85 HMD.InterpupillaryDistance = InterpupillaryDistance;
86 HMD.LensSeparationDistance = 0.0635f;
87 HMD.EyeToScreenDistance = 0.041f;
88 HMD.DistortionK[0] = Distortion.K[0];
89 HMD.DistortionK[1] = Distortion.K[1];
90 HMD.DistortionK[2] = Distortion.K[2];
91 HMD.DistortionK[3] = 0;
93 Set2DAreaFov(DegreeToRad(85.0f));
94 }
96 void StereoConfig::SetFullViewport(const Viewport& vp)
97 {
98 if (vp != FullView)
99 {
100 FullView = vp;
101 DirtyFlag = true;
102 }
103 }
105 void StereoConfig::SetHMDInfo(const HMDInfo& hmd)
106 {
107 HMD = hmd;
108 Distortion.K[0] = hmd.DistortionK[0];
109 Distortion.K[1] = hmd.DistortionK[1];
110 Distortion.K[2] = hmd.DistortionK[2];
111 Distortion.K[3] = hmd.DistortionK[3];
113 Distortion.SetChromaticAberration(hmd.ChromaAbCorrection[0], hmd.ChromaAbCorrection[1],
114 hmd.ChromaAbCorrection[2], hmd.ChromaAbCorrection[3]);
116 if (!IPDOverride)
117 InterpupillaryDistance = HMD.InterpupillaryDistance;
119 DirtyFlag = true;
120 }
122 void StereoConfig::SetDistortionFitPointVP(float x, float y)
123 {
124 DistortionFitX = x;
125 DistortionFitY = y;
126 DirtyFlag = true;
127 }
129 void StereoConfig::SetDistortionFitPointPixels(float x, float y)
130 {
131 DistortionFitX = (4 * x / float(FullView.w)) - 1.0f;
132 DistortionFitY = (2 * y / float(FullView.h)) - 1.0f;
133 DirtyFlag = true;
134 }
136 void StereoConfig::Set2DAreaFov(float fovRadians)
137 {
138 Area2DFov = fovRadians;
139 DirtyFlag = true;
140 }
143 const StereoEyeParams& StereoConfig::GetEyeRenderParams(StereoEye eye)
144 {
145 static const UByte eyeParamIndices[3] = { 0, 0, 1 };
147 updateIfDirty();
148 OVR_ASSERT(eye < sizeof(eyeParamIndices));
149 return EyeRenderParams[eyeParamIndices[eye]];
150 }
153 void StereoConfig::updateComputedState()
154 {
155 // Need to compute all of the following:
156 // - Aspect Ratio
157 // - FOV
158 // - Projection offsets for 3D
159 // - Distortion XCenterOffset
160 // - Update 2D
161 // - Initialize EyeRenderParams
163 // Compute aspect ratio. Stereo mode cuts width in half.
164 Aspect = float(FullView.w) / float(FullView.h);
165 Aspect *= (Mode == Stereo_None) ? 1.0f : 0.5f;
166 Aspect *= AspectMultiplier;
168 updateDistortionOffsetAndScale();
170 // Compute Vertical FOV based on distance, distortion, etc.
171 // Distance from vertical center to render vertical edge perceived through the lens.
172 // This will be larger then normal screen size due to magnification & distortion.
173 //
174 // This percievedHalfRTDistance equation should hold as long as the render target
175 // and display have the same aspect ratios. What we'd like to know is where the edge
176 // of the render target will on the perceived screen surface. With NO LENS,
177 // the answer would be:
178 //
179 // halfRTDistance = (VScreenSize / 2) * aspect *
180 // DistortionFn_Inverse( DistortionScale / aspect )
181 //
182 // To model the optical lens we eliminates DistortionFn_Inverse. Aspect ratios
183 // cancel out, so we get:
184 //
185 // halfRTDistance = (VScreenSize / 2) * DistortionScale
186 //
187 if (Mode == Stereo_None)
188 {
189 YFov = DegreeToRad(80.0f);
190 }
191 else
192 {
193 float percievedHalfRTDistance = (HMD.VScreenSize / 2) * Distortion.Scale;
194 YFov = 2.0f * atan(percievedHalfRTDistance/HMD.EyeToScreenDistance);
195 }
197 updateProjectionOffset();
198 update2D();
199 updateEyeParams();
201 DirtyFlag = false;
202 }
204 void StereoConfig::updateDistortionOffsetAndScale()
205 {
206 // Distortion center shift is stored separately, since it isn't affected
207 // by the eye distance.
208 float lensOffset = HMD.LensSeparationDistance * 0.5f;
209 float lensShift = HMD.HScreenSize * 0.25f - lensOffset;
210 float lensViewportShift = 4.0f * lensShift / HMD.HScreenSize;
211 Distortion.XCenterOffset= lensViewportShift;
213 // Compute distortion scale from DistortionFitX & DistortionFitY.
214 // Fit value of 0.0 means "no fit".
215 if ((fabs(DistortionFitX) < 0.0001f) && (fabs(DistortionFitY) < 0.0001f))
216 {
217 Distortion.Scale = 1.0f;
218 }
219 else
220 {
221 // Convert fit value to distortion-centered coordinates before fit radius
222 // calculation.
223 float stereoAspect = 0.5f * float(FullView.w) / float(FullView.h);
224 float dx = DistortionFitX - Distortion.XCenterOffset;
225 float dy = DistortionFitY / stereoAspect;
226 float fitRadius = sqrt(dx * dx + dy * dy);
227 Distortion.Scale = Distortion.DistortionFn(fitRadius)/fitRadius;
228 }
229 }
231 void StereoConfig::updateProjectionOffset()
232 {
233 // Post-projection viewport coordinates range from (-1.0, 1.0), with the
234 // center of the left viewport falling at (1/4) of horizontal screen size.
235 // We need to shift this projection center to match with the lens center;
236 // note that we don't use the IPD here due to collimated light property of the lens.
237 // We compute this shift in physical units (meters) to
238 // correct for different screen sizes and then rescale to viewport coordinates.
239 float viewCenter = HMD.HScreenSize * 0.25f;
240 float eyeProjectionShift = viewCenter - HMD.LensSeparationDistance*0.5f;
241 ProjectionCenterOffset = 4.0f * eyeProjectionShift / HMD.HScreenSize;
242 }
244 void StereoConfig::update2D()
245 {
246 // Orthographic projection fakes a screen at a distance of 0.8m from the
247 // eye, where hmd screen projection surface is at 0.05m distance.
248 // This introduces an extra off-center pixel projection shift based on eye distance.
249 // This offCenterShift is the pixel offset of the other camera's center
250 // in your reference camera based on surface distance.
251 float metersToPixels = (HMD.HResolution / HMD.HScreenSize);
252 float lensDistanceScreenPixels= metersToPixels * HMD.LensSeparationDistance;
253 float eyeDistanceScreenPixels = metersToPixels * InterpupillaryDistance;
254 float offCenterShiftPixels = (HMD.EyeToScreenDistance / 0.8f) * eyeDistanceScreenPixels;
255 float leftPixelCenter = (HMD.HResolution / 2) - lensDistanceScreenPixels * 0.5f;
256 float rightPixelCenter = lensDistanceScreenPixels * 0.5f;
257 float pixelDifference = leftPixelCenter - rightPixelCenter;
259 // This computes the number of pixels that fit within specified 2D FOV (assuming
260 // distortion scaling will be done).
261 float percievedHalfScreenDistance = tan(Area2DFov * 0.5f) * HMD.EyeToScreenDistance;
262 float vfovSize = 2.0f * percievedHalfScreenDistance / Distortion.Scale;
263 FovPixels = HMD.VResolution * vfovSize / HMD.VScreenSize;
265 // Create orthographic matrix.
266 Matrix4f& m = OrthoCenter;
267 m.SetIdentity();
268 m.M[0][0] = FovPixels / (FullView.w * 0.5f);
269 m.M[1][1] = -FovPixels / FullView.h;
270 m.M[0][3] = 0;
271 m.M[1][3] = 0;
272 m.M[2][2] = 0;
274 float orthoPixelOffset = (pixelDifference + offCenterShiftPixels/Distortion.Scale) * 0.5f;
275 OrthoPixelOffset = orthoPixelOffset * 2.0f / FovPixels;
276 }
278 void StereoConfig::updateEyeParams()
279 {
280 // Projection matrix for the center eye, which the left/right matrices are based on.
281 Matrix4f projCenter = Matrix4f::PerspectiveRH(YFov, Aspect, 0.01f, 2000.0f);
283 switch(Mode)
284 {
285 case Stereo_None:
286 {
287 EyeRenderParams[0].Init(StereoEye_Center, FullView, 0, projCenter, OrthoCenter);
288 }
289 break;
291 case Stereo_LeftRight_Multipass:
292 {
293 Matrix4f projLeft = Matrix4f::Translation(ProjectionCenterOffset, 0, 0) * projCenter,
294 projRight = Matrix4f::Translation(-ProjectionCenterOffset, 0, 0) * projCenter;
296 EyeRenderParams[0].Init(StereoEye_Left,
297 Viewport(FullView.x, FullView.y, FullView.w/2, FullView.h),
298 +InterpupillaryDistance * 0.5f, // World view shift.
299 projLeft, OrthoCenter * Matrix4f::Translation(OrthoPixelOffset, 0, 0),
300 &Distortion);
301 EyeRenderParams[1].Init(StereoEye_Right,
302 Viewport(FullView.x + FullView.w/2, FullView.y, FullView.w/2, FullView.h),
303 -InterpupillaryDistance * 0.5f,
304 projRight, OrthoCenter * Matrix4f::Translation(-OrthoPixelOffset, 0, 0),
305 &Distortion);
306 }
307 break;
308 }
310 }
313 }}} // OVR::Util::Render