1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
/**
* Copyright 2014 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Compliance with Oculus VR RIFT SDK LICENSE (see below)
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*
* This file contains mathematical equations, comments and algorithms
* used in the Oculus VR RIFT SDK 0.3.2.
*
* Due to unknown legal status, the 'requested' Copyright tag and disclaimer
* below has been added.
*
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*
* Copyright © 2014 Oculus VR, Inc. All rights reserved
*
* Oculus VR, Inc. Software Development Kit License Agreement
*
* Human-Readable Summary*:
*
* You are Free to:
*
* Use, modify, and distribute the Oculus VR Rift SDK in source and binary
* form with your applications/software.
*
* With the Following Restrictions:
*
* You can only distribute or re-distribute the source code to LibOVR in
* whole, not in part.
*
* Modifications to the Oculus VR Rift SDK in source or binary form must
* be shared with Oculus VR.
*
* If your applications cause health and safety issues, you may lose your
* right to use the Oculus VR Rift SDK, including LibOVR.
*
* The Oculus VR Rift SDK may not be used to interface with unapproved commercial
* virtual reality mobile or non-mobile products or hardware.
* * - This human-readable Summary is not a license. It is simply a convenient
* reference for understanding the full Oculus VR Rift SDK License Agreement.
* The Summary is written as a user-friendly interface to the full Oculus VR Rift
* SDK License below. This Summary itself has no legal value, and its contents do
* not appear in the actual license.
*
* Full-length Legal Copy may be found at:
* http://www.oculusvr.com/licenses/LICENSE-3.1
* http://jogamp.org/git/?p=oculusvr-sdk.git;a=blob;f=LICENSE.txt;hb=HEAD
* Or within this repository: oculusvr-sdk/LICENSE.txt
*
* THIS RIFT SDK AND ANY COMPONENT THEREOF IS PROVIDED BY OCULUS VR AND
* ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OCULUS VR AS THE
* COPYRIGHT OWNER OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS RIFT
* SDK OR THE RIFT SDK DERIVATIVES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jogamp.opengl.oculusvr.stereo.lense;
import com.jogamp.math.VectorUtil;
import com.jogamp.nativewindow.util.DimensionImmutable;
import com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceConfig;
public class DistortionSpec {
public DistortionSpec(final LensConfig lens) {
this.lens = lens;
this.pixelsPerTanAngleAtCenter = new float[2];
this.tanEyeAngleScale = new float[2];
this.lensCenter = new float[2];
}
private static final float[] quaterHalf = new float[] { 0.25f, 0.5f };
final LensConfig lens;
final float[] pixelsPerTanAngleAtCenter;
final float[] tanEyeAngleScale;
final float[] lensCenter;
public static DistortionSpec[] CalculateDistortionSpec (final GenericStereoDeviceConfig deviceConfig, final float[] eyeReliefInMeters) {
final DistortionSpec[] result = new DistortionSpec[2];
/// FIXME: Add 'pluggable' lense configuration
final LensConfig[] lensConfig = LensConfig.GenerateLensConfigFromEyeRelief(eyeReliefInMeters, LensConfig.DistortionEquation.CatmullRom10);
result[0] = CalculateDistortionSpec (deviceConfig, 0, eyeReliefInMeters[0], lensConfig[0]);
result[1] = CalculateDistortionSpec (deviceConfig, 1, eyeReliefInMeters[1], lensConfig[1]);
return result;
}
private static DistortionSpec CalculateDistortionSpec (final GenericStereoDeviceConfig deviceConfig, final int eyeName,
final float eyeReliefInMeters, final LensConfig lensConfig) {
// From eye relief, IPD and device characteristics, we get the distortion mapping.
// This distortion does the following things:
// 1. It undoes the distortion that happens at the edges of the lens.
// 2. It maps the undistorted field into "retina" space.
// So the input is a pixel coordinate - the physical pixel on the display itself.
// The output is the real-world direction of the ray from this pixel as it comes out of the lens and hits the eye.
// However we typically think of rays "coming from" the eye, so the direction (TanAngleX,TanAngleY,1) is the direction
// that the pixel appears to be in real-world space, where AngleX and AngleY are relative to the straight-ahead vector.
// If your renderer is a raytracer, you can use this vector directly (normalize as appropriate).
// However in standard rasterisers, we have rendered a 2D image and are putting it in front of the eye,
// so we then need a mapping from this space to the [-1,1] UV coordinate space, which depends on exactly
// where "in space" the app wants to put that rendertarget.
// Where in space, and how large this rendertarget is, is completely up to the app and/or user,
// though of course we can provide some useful hints.
// TODO: Use IPD and eye relief to modify distortion (i.e. non-radial component)
// TODO: cope with lenses that don't produce collimated light.
// This means that IPD relative to the lens separation changes the light vergence,
// and so we actually need to change where the image is displayed.
final DistortionSpec localDistortion = new DistortionSpec(lensConfig);
final DimensionImmutable resolutionInPixels = deviceConfig.surfaceSizeInPixels;
final float[] pixelsPerMeter = new float[] { resolutionInPixels.getWidth() / deviceConfig.screenSizeInMeters[0],
resolutionInPixels.getHeight() / deviceConfig.screenSizeInMeters[1] };
VectorUtil.scaleVec2(localDistortion.pixelsPerTanAngleAtCenter, pixelsPerMeter, localDistortion.lens.MetersPerTanAngleAtCenter);
// Same thing, scaled to [-1,1] for each eye, rather than pixels.
VectorUtil.scaleVec2(localDistortion.tanEyeAngleScale, quaterHalf,
VectorUtil.divVec2(new float[2], deviceConfig.screenSizeInMeters, localDistortion.lens.MetersPerTanAngleAtCenter) );
// <--------------left eye------------------><-ScreenGapSizeInMeters-><--------------right eye----------------->
// <------------------------------------------ScreenSizeInMeters.Width----------------------------------------->
// <------------interpupillaryDistanceInMeters-------------->
// <--centerFromLeftInMeters->
// ^
// Center of lens
// Find the lens centers in scale of [-1,+1] (NDC) in left eye.
final float visibleWidthOfOneEye = 0.5f * ( deviceConfig.screenSizeInMeters[0] );
final float centerFromLeftInMeters = ( deviceConfig.screenSizeInMeters[0] - deviceConfig.interpupillaryDistanceInMeters ) * 0.5f;
localDistortion.lensCenter[0] = ( centerFromLeftInMeters / visibleWidthOfOneEye ) * 2.0f - 1.0f;
localDistortion.lensCenter[1] = ( deviceConfig.pupilCenterFromScreenTopInMeters / deviceConfig.screenSizeInMeters[1] ) * 2.0f - 1.0f;
if ( 1 == eyeName ) {
localDistortion.lensCenter[0] = -localDistortion.lensCenter[0];
}
return localDistortion;
}
}
|