forked from microsoft/DirectXMath
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStereo3DMatrixHelper.cpp
261 lines (220 loc) · 10.3 KB
/
Stereo3DMatrixHelper.cpp
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//-------------------------------------------------------------------------------------
// Stereo3DMatrixHelper.cpp -- SIMD C++ Math helper for Stereo 3D matricies
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-------------------------------------------------------------------------------------
#include "Stereo3DMatrixHelper.h"
using namespace DirectX;
namespace
{
inline bool StereoProjectionHelper
(
const STEREO_PARAMETERS& stereoParameters,
_Out_ float* fVirtualProjection,
_Out_ float* zNearWidth,
_Out_ float* zNearHeight,
float FovAngleY,
float AspectRatio,
float NearZ
)
{
// note that most people have difficulty fusing images into 3D
// if the separation equals even just the human average. by
// reducing the separation (interocular distance) by 1/2, we
// guarantee a larger subset of people will see full 3D
// the conservative setting should always be used. the only problem
// with the conservative setting is that the 3D effect will be less
// impressive on smaller screens (which makes sense, since your eye
// cannot be tricked as easily based on the smaller fov). to simulate
// the effect of a larger screen, use the liberal settings (debug only)
// Conservative Settings: * max acuity angle: 0.8f degrees * interoc distance: 1.25 inches
// Liberal Settings: * max acuity angle: 1.6f degrees * interoc distance: 2.5f inches
// maximum visual accuity angle allowed is 3.2 degrees for
// a physical scene, and 1.6 degrees for a virtual one.
// thus we cannot allow an object to appear any closer to
// the viewer than 1.6 degrees (divided by two for most
// half-angle calculations)
static const float fMaxStereoDistance = 780; // inches (should be between 10 and 20m)
static const float fMaxVisualAcuityAngle = 1.6f * (XM_PI / 180.0f); // radians
static const float fInterocularDistance = 1.25f; // inches
float fDisplayHeight = stereoParameters.fDisplaySizeInches / sqrtf(AspectRatio * AspectRatio + 1.0f);
float fDisplayWidth = fDisplayHeight * AspectRatio;
float fHalfInterocular = 0.5f * fInterocularDistance * stereoParameters.fStereoExaggerationFactor;
float fHalfPixelWidth = fDisplayWidth / stereoParameters.fPixelResolutionWidth * 0.5f;
float fHalfMaximumAcuityAngle = fMaxVisualAcuityAngle * 0.5f * stereoParameters.fStereoExaggerationFactor;
// float fHalfWidth = fDisplayWidth * 0.5f;
float fMaxSeparationAcuityAngle = atanf(fHalfInterocular / fMaxStereoDistance);
float fMaxSeparationDistance = fHalfPixelWidth / tanf(fMaxSeparationAcuityAngle);
float fRefinedMaxStereoDistance = fMaxStereoDistance - fMaxSeparationDistance;
float fFovHalfAngle = FovAngleY / 2.0f;
bool ComfortableResult = true;
if (fRefinedMaxStereoDistance < 0.0f || fMaxSeparationDistance > 0.1f * fMaxStereoDistance)
{
// Pixel resolution is too low to offer a comfortable stereo experience
ComfortableResult = false;
}
float fRefinedMaxSeparationAcuityAngle = atanf(fHalfInterocular / (fRefinedMaxStereoDistance));
float fPhysicalZNearDistance = fHalfInterocular / tanf(fHalfMaximumAcuityAngle);
// float fScalingFactor = fHalfMaximumAcuityAngle / atanf(fHalfInterocular / stereoParameters.fViewerDistanceInches);
float fNearZSeparation = tanf(fRefinedMaxSeparationAcuityAngle) * (fRefinedMaxStereoDistance - fPhysicalZNearDistance);
// float fNearZSeparation2 = fHalfInterocular * (fRefinedMaxStereoDistance - fPhysicalZNearDistance) / fRefinedMaxStereoDistance;
(*zNearHeight) = cosf(fFovHalfAngle) / sinf(fFovHalfAngle);
(*zNearWidth) = (*zNearHeight) / AspectRatio;
(*fVirtualProjection) = (fNearZSeparation * NearZ * (*zNearWidth * 4.0f)) / (2.0f * NearZ);
return ComfortableResult;
}
}
//------------------------------------------------------------------------------
void DirectX::StereoCreateDefaultParameters
(
STEREO_PARAMETERS& stereoParameters
)
{
// Default assumption is 1920x1200 resolution, a 22" LCD monitor, and a 2' viewing distance
stereoParameters.fViewerDistanceInches = 24.0f;
stereoParameters.fPixelResolutionWidth = 1920.0f;
stereoParameters.fPixelResolutionHeight = 1200.0f;
stereoParameters.fDisplaySizeInches = 22.0f;
stereoParameters.fStereoSeparationFactor = 1.0f;
stereoParameters.fStereoExaggerationFactor = 1.0f;
}
//------------------------------------------------------------------------------
XMMATRIX DirectX::StereoProjectionFovLH
(
_In_opt_ const STEREO_PARAMETERS* pStereoParameters,
STEREO_CHANNEL Channel,
float FovAngleY,
float AspectRatio,
float NearZ,
float FarZ,
STEREO_MODE StereoMode
)
{
assert(Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT);
assert(StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED);
assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f));
assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f));
assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
STEREO_PARAMETERS DefaultParameters = {};
if (pStereoParameters == nullptr)
{
StereoCreateDefaultParameters(DefaultParameters);
pStereoParameters = &DefaultParameters;
}
assert(pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f);
assert(pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f);
float fVirtualProjection = 0.0f;
float zNearWidth = 0.0f;
float zNearHeight = 0.0f;
StereoProjectionHelper(*pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectRatio, NearZ);
fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias
//
// By applying a translation, we are forcing our cameras to be parallel
//
float fInvertedAngle = atanf(fVirtualProjection / (2.0f * NearZ));
XMMATRIX proj = XMMatrixPerspectiveFovLH(FovAngleY, AspectRatio, NearZ, FarZ);
XMMATRIX patchedProjection;
if (Channel == STEREO_CHANNEL_LEFT)
{
if (StereoMode > STEREO_MODE_NORMAL)
{
XMMATRIX rots = XMMatrixRotationY(fInvertedAngle);
XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj);
}
else
{
XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(trans, proj);
}
}
else
{
if (StereoMode > STEREO_MODE_NORMAL)
{
XMMATRIX rots = XMMatrixRotationY(-fInvertedAngle);
XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj);
}
else
{
XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(trans, proj);
}
}
return patchedProjection;
}
//------------------------------------------------------------------------------
XMMATRIX DirectX::StereoProjectionFovRH
(
_In_opt_ const STEREO_PARAMETERS* pStereoParameters,
STEREO_CHANNEL Channel,
float FovAngleY,
float AspectRatio,
float NearZ,
float FarZ,
STEREO_MODE StereoMode
)
{
assert(Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT);
assert(StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED);
assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f));
assert(!XMScalarNearEqual(AspectRatio, 0.0f, 0.00001f));
assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
STEREO_PARAMETERS DefaultParameters = {};
if (pStereoParameters == nullptr)
{
StereoCreateDefaultParameters(DefaultParameters);
pStereoParameters = &DefaultParameters;
}
assert(pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f);
assert(pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f);
float fVirtualProjection = 0.0f;
float zNearWidth = 0.0f;
float zNearHeight = 0.0f;
StereoProjectionHelper(*pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectRatio, NearZ);
fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias
//
// By applying a translation, we are forcing our cameras to be parallel
//
float fInvertedAngle = atanf(fVirtualProjection / (2.0f * NearZ));
XMMATRIX proj = XMMatrixPerspectiveFovRH(FovAngleY, AspectRatio, NearZ, FarZ);
//
// By applying a translation, we are forcing our cameras to be parallel
//
XMMATRIX patchedProjection;
if (Channel == STEREO_CHANNEL_LEFT)
{
if (StereoMode > STEREO_MODE_NORMAL)
{
XMMATRIX rots = XMMatrixRotationY(fInvertedAngle);
XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj);
}
else
{
XMMATRIX trans = XMMatrixTranslation(-fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(trans, proj);
}
}
else
{
if (StereoMode > STEREO_MODE_NORMAL)
{
XMMATRIX rots = XMMatrixRotationY(-fInvertedAngle);
XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(XMMatrixMultiply(rots, trans), proj);
}
else
{
XMMATRIX trans = XMMatrixTranslation(fVirtualProjection, 0, 0);
patchedProjection = XMMatrixMultiply(trans, proj);
}
}
return patchedProjection;
}