156 10.PracticalStereoRendering
Setting the near plane n of the viewing frustum can be done based on the de-
sired acceptable negative parallax. The maximum negative parallax should not
exceed the interaxial distance. A relationship between these distances can be es-
tablished by using similar triangles and limits.
As another example, we calculate the parallax at
0
zz
,
0
2
zz
, z , and
0z .
By definition, the parallax at
0
z
is zero. The parallax can be solved for the
other values by similar triangles:
00
a
Wzz
Wz
,
0
0
1
a
z
WW
z

.
For
0
2
zz ,
0
0
1
aa
z
WW W
z

.
For z ,
0
0
lim 1
aa
z
z
WW W
z


.
For 0z ,
0
0
0
lim 1
a
z
z
WW
z

.
As z approaches
, the horizontal parallax approaches the interaxial distance,
which is perceived by the eyes as a far-away object. As z approaches zero, the
horizontal parallax approaches
. For this reason, introducing excessive nega-
tive parallax in the scene adds the risk that the eyes will not be able to converge
the two images. To prevent this, set the near plane to
0
2z , which results in a
negative horizontal parallax equal to the interaxial distance.
In most applications, the camera is designed for monoscopic viewing and is
modified for left and right eyes only when stereo rendering is enabled. Given a
camera at the monoscopic (center) position, it is straightforward to calculate the
left and right plane offsets at the near plane based on the camera offset. Figure
10.4 shows the offset for the left camera. Note that shifting the camera horizon-
tally results in an equal shift at the zero-parallax line.
10.4TheMathematicsofStereoViewsandProjection 157
Figure 10.4. Offsetting the left and right frustum planes to form an asymmetric matrix
with zero parallax. (For clarity, the right eye is not shown.)
To set up the left and right cameras, offset the left and right cameras horizon-
tally by half the interaxial distance. For projection, offset the left and right frus-
tum planes for each eye in order to setup the asymmetric frustum.
Given a horizontal camera offset of
Δ
a
W
, we can calculate the
Δ
n
W
offset for
the left and right projection planes by using similar triangles, as follows:
0
Δ
Δ
n
a
Wn
Wz
.
Therefore,
0
Δ
Δ
na
n
WW
z
.
Listing 10.1 outlines the pseudocode for modifying the view frustum and
camera transformation for stereoscopic viewing.
θθ
Δ
a
W
Δ
a
W Δ
a
W
Δ
n
W Δ
n
W
0
Δ
W
0
z
n
Left Center
158 10.PracticalStereoRendering
// Camera structure (left-handed space).
struct Camera
{
float3 up; // camera up direction (normalized)
float3 dir; // camera look-at direction (normalized)
float3 pos; // camera position
float aspect; // aspect ratio (w/h)
float fov; // horizontal field of view (radians)
float Wa; // camera interaxial distance (stereo)
float z0; // camera distance to zero parallax
float zn; // camera frustum near plane (screen projection)
float zf; // camera frustum far plane
float l; // camera frustum left plane
float r; // camera frustum right plane
float t; // camera frustum top plane
float b; // camera frustum bottom plane
};
// Create a view-projection matrix from camera.
matrix getViewProjMatrix(const Camera& camera)
{
matrix viewMatrix;
matrix projMatrix;
matrix viewProjMatrix;
// Build look-at view transformation matrix.
getCameraMatrix(&viewMatrix, &camera.pos, &camera.dir, &camera.up);
// Build off-center projection transformation matrix.
getPerspectiveMatrix(&projMatrix, camera.l, camera.r, camera.b,
camera.t, camera.zn, camera.zf);
// Multiply view matrix by projection matrix.
matrixMultiply(&viewProjMatrix, &viewMatrix, &projMatrix);
return (viewProjMatrix);
}
10.5UsingGeometryShadertoRenderStereoPairs 159
// Creating center/left/right view-projection matrices.
void buildViewProjectionMatrices()
{
// Get current monoscopic camera in scene.
Camera camera = getCurrentCamera();
// Create right vector from normalized up and direction vector.
float3 right = Cross(&camera.up, &camera.dir);
// Calculate horizontal camera offsets and frustum plane offsets.
float3 cameraOffset = 0.5F * camera.Wa * right;
float planeOffset = 0.5F * camera.Wa * camera.zn / camera.z0;
// Create left eye camera from center camera.
Camera cameraLeft = camera;
cameraLeft.pos -= cameraOffset;
cameraLeft.l += planeOffset;
cameraLeft.r += planeOffset;
// Create right eye camera from center camera.
Camera cameraRight = camera;
cameraRight.pos += 0.5F * camera.Wa * right;
cameraRight.l -= planeOffset;
cameraRight.r -= planeOffset;
// Store camera view-projection matrices.
g_viewProjMatrix = getViewProjMatrix(camera);
g_viewProjMatrixLeft = getViewProjMatrix(cameraLeft);
g_viewProjMatrixRight = getViewProjMatrix(cameraRight);
}
Listing 10.1. Pseudocode for camera structure (left-handed space).
10.5UsingGeometryShadertoRenderStereoPairs
On some stereo display formats, the left eye and right eye may be packed in the
same back buffer, and we can use viewports to control which eye to render to. In
Direct3D 10 and OpenGL 4.1, the geometry shader can be used to generate ge-
ometry as well as redirect output to a specific viewport. Instead of rendering ste-
reo in multiple passes, one can use the geometry shader to generate geometry for
160 10.PracticalStereoRendering
each eye. This rendering technique may improve performance, especially if the
geometry shader is being used anyway.
One method could be to use geometry amplification to do this. This requires
that we perform the view-projection transformations in the geometry shader,
which may not be as efficient as instancing if the geometry shader is the bottle-
neck. Another way is to use geometry instancing and write a pass-through geom-
etry shader. Sample HLSL code for Direct3D 10 is shown in Listing 10.2. To
render with instancing, set the geometry instanced count to two, as follows:
pD3DDevice->DrawIndexedInstanced(numIndices, 2, 0, 0, 0);
matrix WorldViewProjLeft;
matrix WorldViewProjRight;
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float4 Color : COLOR0;
uint InstanceId : INSTANCE;
};
struct GS_OUTPUT
{
float4 Pos : SV_POSITION;
float4 Color : COLOR0;
uint InstanceId : INSTANCE;
uint Viewport : SV_ViewportArrayIndex;
};
PS_INPUT VS(float4 Pos : POSITION, float4 Color : COLOR,
uint InstanceId : SV_InstanceID)
{
PS_INPUT input;
if (InstanceId == 0)
{
input.Pos = mul(input.Pos, WorldViewProjLeft);
}
else
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.165.62