CrystalEyes TM Software Development Kit
A StereoGraphics ® Product
Updated: December 1, 1997
THE INFORMATION IN THIS MANUAL IS SUBJECT TO CHANGE WITHOUT NOTICE AND IS DISTRIBUTED ON AN
"AS IS" BASIS, WITHOUT WARRANTY. WHILE EVERY PRECAUTION HAS BEEN TAKEN IN THE PREPARATION
OF THIS MANUAL, NEITHER THE AUTHORS NOR STEREOGRAPHICS CORPORATION SHALL HAVE ANY
LIABILITY, LOSS, OR DAMAGE CAUSED OR ALLEGED TO BE CAUSED DIRECTLY OR INDIRECTLY BY THE
INFORMATION CONTAINED HEREIN.
Acknowledgments
StereoGraphics would like to acknowledge the following people for their contributions to this document: Harvey Ziegler and
John Stevens at Hewlett-Packard, Michael Adams at Digital Equipment Corporation, Craig Winter and Maggie Bourget at
EDS Unigraphics, John Schimpf at Silicon Graphics, Dave Milici and Lenny Lipton at StereoGraphics, Bob Akka at Chasm
Graphics, Andy Woolf and Carsten R. at Diamond Multimedia Systems, and Geoff Hennessey at PCI Incorporated.
Copyright Notice
This manual is copyrighted by StereoGraphics Corporation. Copyright © 1997 StereoGraphics Corporation. All rights
reserved. Printed in the United States of America.
Limited Warranty
THIS DOCUMENT AND ASSOCIATED PROGRAMS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND
EITHER EXPRESSED OR IMPLIED. THE ENTIRE RISK AS TO THE RESULTS AND PERFORMANCE OF THE
SOFTWARE IS ASSUMED BY YOU. STEREOGRAPHICS DOES NOT WARRANT THAT THE FUNCTION OF THE
SOFTWARE WILL MEET WITH YOUR REQUIREMENTS OR THAT THE SOFTWARE WILL FUNCTION
UNINTERRUPTED AND ERROR FREE.
StereoGraphics is a registered trademark of StereoGraphics Corporation; CrystaEyes is a trademark of StereoGraphics
Corporation. All product and brand names mentioned in this document are trademarks or registered trademarks of their
respective holders.
StereoGraphics Corporation
2171 East Francisco Boulevard
San Rafael, CA 94901
(415) 459-4500
Email: develop@crystaleye.com
Web: www.stereographics.com
Soft copies of this document are available on the accompanying disk
(non web/ftp site version), or via the StereoGraphics web site
Updated versions of this document are available at:
www. stereographics.com
StereoGraphics CrystalEyes Software Development Kit Page 2
CrystalEyes Software Development Kit
Table of Contents
TABLE OF CONTENTS - CRYSTALEYES SOFTWARE DEVELOPMENT KIT 3
PREFACE - STEREOSCOPIC DISPLAY SUPPORT IN OPEN GL 5
CHAPTER 1 7
SGI GL Development 7
Introduction 7
Checking Stereoscopic Hardware 8
Four Different SGI Modes 9
Switching To and From Stereo Display Mode 10
Opening a Window for Stereoscopic Display 12
Stereoscopic Viewporting and Buffering 13
Stereoscopic Projections 15
Cursor and Interface Issues 17
Sample GL Application 18
CHAPTER 2 23
SGI OpenGL Development 23
Stereo Support Under OpenGL 23
Sample OpenGL Application 24
CHAPTER 3 33
SUN XGL Development 33
Introduction 33
Checking Stereoscopic Hardware 34
Configuring the Frame Buffer for Stereo Display Mode 35
Rendering the Left and Right Eyes 36
Two Dimensional Rendering 37
Stereoscopic Projections 38
Sample XGL Application 39
CHAPTER 4 49
DEC OpenGL Development 49
Introduction 49
Stereo Support 50
Enabling Stereo from an Application 51
Supported Screen Resolutions and Refresh Rates 52
Supported Hardware 56
Supported Visuals 57
Backing Store 58
DEC OpenGL Development (cont.)
StereoGraphics CrystalEyes Software Development Kit Page 3
Performance 59
Left and Right Construction Planes 60
Sample OpenGL Application 61
CHAPTER 5 65
PC Development 65
Introduction 65
Sample Windows NT OpenGL Application 66
OpenGL Stereo Quad Buffering Under Windows NT 69
Sample OpenGL Application for Diamond Multimedia Systems' FireGL 4000 Graphics Board 71
Creating Stereoscopic Renderings Using Kinetix 3D Studio Products and Autodesk Animator Pro 74
CHAPTER 6 81
HP Open GL Development 81
Introduction 81
Supported Graphics Cards 82
Supported Monitors 83
Enabling Stereo Support 84
StereoGraphics CrystalEyes Software Development Kit Page 4
Preface
Stereoscopic Display Support in Open GL
With the release of OpenGL version 1.1 specification, stereoscopic display support is readily
programmable through APIs common to all graphics workstations. The same OpenGL APIs which support
double buffering for real-time 3D rendering also support stereo quad buffering on stereo-ready systems.
Identification of stereo-capable display modes is queried via glGetBooleanv() with GL_STEREO
parameter.
Whereas double buffering assigns a visible front buffer and a non-visible back buffer, stereo quad
buffering additionally assigns left and right buffer components. Instead of referring to the double buffers
as GL_FRONT and GL_BACK, the stereo buffers are individually identified as GL_FRONT_LEFT and
GL_FRONT_RIGHT, GL_BACK_LEFT, and GL_BACK_RIGHT. The underlying graphics hardware
automatically alternates between the left and right front buffer displays. Typically the programmer selects
only the left and right back buffers for drawing with glDrawBuffer(), and then updates the complete stereo
pair with a system swap function, such as glXSwapBuffers() or glutSwapBuffers().
While OpenGL specifies system-independent functions for primitive 3D rendering operations, system-
specific functions like setting the display modes are deferred into separate libraries, like GLX for X-
Windows, or GLUT for any compatible system. In GLX, a stereo display mode is requested by adding
GLX_STEREO flag for glXChooseVisual(), which is then enabled by subsequent calls to
glXCreateContext() and glXMakeCurrent(). In GLUT, the equivalent sequence of calls is handled by
passing GLUT_STEREO flag to glutInitDisplayMode().
Under Microsoft Windows NT/9x, an equivalent set of WIN32 system APIs are available as counterparts
to the GLX APIs: ChoosePixelFormat(), wglCreateContext(), and wglMakeCurrent(). A stereo display
mode is requested by adding the PFD_STEREO flag to the OpenGL-specified Pixel Format Descriptor
used in ChoosePixelFormat(). OpenGL graphics boards with mini client drivers for intercepting OpenGL
calls with hardware support will be able to enable GL_STEREO quad buffering modes in the same
manner.
For further information about OpenGL, the following reference textbooks are currently available from the
OpenGL Architecture Review Board, published by Addison Wesley.
* OpenGL Reference Manual, by the OpenGL Architecture Review Board, Renate Kempf and Chris
Frazier, Editors.
* OpenGL Programming Guide, by Jackie Neider, Tom Davis, and Mason Woo.
* OpenGL Programming for the X-Windows System, by Mark J. Kilgard.
For information about OpenGL on Microsoft Windows NT/9x, refer to the WIN32 SDK included with all
commercial C compilers supporting MS Windows platforms, and the OpenGL SDK available from the
Microsoft Developer Network, MSDN.
StereoGraphics CrystalEyes Software Development Kit Page 5
StereoGraphics CrystalEyes Software Development Kit Page 6
Chapter 1
SGI GL
1. Introduction
Nearly all SGI hardware systems built in the last five years (including all systems currently being shipped)
include built-in support for StereoGraphics CrystalEyes compatible stereoscopy, the most notable
exceptions being some of the early Indigo models that operate at resolutions lower than 1280 x 1024.
Furthermore, such stereo-ready Silicon Graphics computer systems have also been shipped with monitors
that are designed to handle the higher vertical frequency that CrystalEyes requires. This nearly universal
support for stereoscopic graphics makes SGI a very attractive platform for developing stereoscopic
applications.
This document discusses methods for developing stereoscopic software on SGI systems using GL,
including:
* How to check whether the hardware supports stereo
* Converting to and from stereo display mode
* Opening a window for stereoscopic display
* Viewporting for left and right eyes
* Stereoscopic perspective projections
* Interface issues
* Sample GL Application
Many of the concepts discussed in this document are illustrated in a short example program, whose source
code should accompany this document.
StereoGraphics CrystalEyes Software Development Kit Page 7
2. Checking Stereoscopic Hardware
This section discusses how an application ought to check the stereoscopic compatibility of a user's
computer system. If you would like to test your own computer's stereo compatibility, attempt to switch in
and out of stereo mode using setmon (see section 4); if the system is not stereo-ready, you will most
likely see no change.
Nearly all SGI computers that are currently in use include system support for stereoscopic display. Some
older models, and a few lower priced recent models, do not support stereo. Application software should
check whether the system is stereo-ready before attempting to display stereoscopic graphics. Here's how:
testval = (int) getgdesc (GD_STEREO);
If the return value equals 1 (TRUE ), then the computer system is stereo-ready. If the return value equals 0
(FALSE ), then it is not stereo-ready, and your application should not attempt to switch to a stereoscopic
display mode.
Certain high-end SGI computer systems (some Crimson, VTX, Onyx models) include support for
windowed square-pixel stereoscopy. To check whether this higher level of stereo support is available, use:
testval = (int) getgdesc (GD_STEREO_IN_WINDOW);
StereoGraphics CrystalEyes Software Development Kit Page 8
3. Four Different SGI Stereo Modes
SGI has not one, but four different stereoscopic display modes. Three of these are available on most SGI
computer systems, and all four are available on certain high-end models.
STR_RECT:
The most common SGI stereo mode is the one that has been available the longest, STR_RECT. In this
stereo mode, the vertical sync rate of the monitor is doubled from its normal display rate, and:
* Everything that is drawn to the top half (actually, the upper 492 out of 1024 pixel rows, a little less
than half) of the normal display will appear to fill the screen for the left eye only.
* Everything that is drawn to the bottom half (actually, the lower 492 out of 1024 pixel rows) of the
normal display will appear to fill the screen for the right eye only.
* The middle 40 pixel rows disappear into the display blanking interval.
STR_TOP:
This newer stereo mode allows stereoscopic windowing. In this stereo mode:
* Everything that is drawn to the top half (the upper 492 out of 1024 pixel rows) of the normal display
will appear to fill the screen for both eyes, unless the contents of windows have been programmed
to have both left- and right-eye buffers, in which case the left- and right-eye buffers will each be
displayed to the appropriate eye.
Programming for STR_TOP display mode requires using SGI extensions to OpenGL.
STR_BOT:
This stereo mode is just like STR_TOP, except that the bottom half (492 pixel rows) of the normal
display, rather than the top half, fill the screen for both eyes.
Programming for STR_BOT display mode requires using SGI extensions to OpenGL.
Square-pixel stereo:
Some high-end SGI models (those for which getgdesc (GD_STEREO_IN_WINDOW) returns TRUE)
offer a fully double-buffered stereo windowing mode, though at somewhat lower than normal stereoscopic
resolution.
StereoGraphics CrystalEyes Software Development Kit Page 9
4. Switching To and From Stereo Display Mode
Switching to and from stereoscopic display mode may be done by the application, or by the user from the
command line.
To manually switch to stereo mode, type one of the following at the command line (see the above section
about the difference between these stereo modes):
/usr/gfx/setmon STR_RECT
/usr/gfx/setmon STR_TOP
/usr/gfx/setmon STR_BOT
Before doing this, be sure that you will have a convenient command prompt to switch back out of stereo
mode. For example, if switching to STR_TOP stereo mode, make sure that you will have a command
prompt available on the top half of the display.
You would also use /usr/gfx/setmon to manually switch to square-pixel stereo, on those SGI models
that support it. Consult SGI documentation, including man setmon and man stereo, for further
details.
To manually switch out of stereo mode, type one of the following at the command line:
/usr/gfx/setmon 60HZ
/usr/gfx/setmon 72HZ
If you are not sure whether your system normally uses a 72 Hz vertical frequency, use 60HZ.
It is generally far more elegant for your application to switch to and from stereo mode on its own, without
requiring the user to shell out and use setmon. To switch display modes using GL, simply use the
setmonitor() function:
setmonitor (STR_RECT);
setmonitor (STR_TOP);
setmonitor (STR_BOT);
setmonitor() does not work for switching to square-pixel stereo. For systems that support square-
pixel stereo, your code can call /usr/gfx/setmon using the system() function.
Before switching to stereo mode using setmonitor(), use getmonitor() to find out the previous
monitor mode:
prev_monitor = (short) getmonitor ();
To switch out of stereo mode, simply setmonitor() to the previous monitor mode:
setmonitor (prev_monitor);
StereoGraphics CrystalEyes Software Development Kit Page 10
If the previous mode is unknown, or to switch to normal monoscopic display mode regardless of the
previous mode, use:
setmonitor (HZ60);
Note again that GL does not provide programming support for STR_TOP or STR_BOT stereo display
modes. Programming for these display mode requires using SGI extensions to OpenGL.
StereoGraphics CrystalEyes Software Development Kit Page 11
5. Opening a Window for Stereoscopic Display
You may create a window for doing stereo using the same methods that you normally do. The differences
that are specific to stereo are:
* STR_RECT display mode: The display window needs to be full-screen, since anything else on the
display will not view properly while the display is in this mode.
* STR_TOP display mode: The display window should either fill or be entirely contained within the
upper half (492 pixel rows) of the normal display, since the remainder of the normal display does
not appear in stereo mode. Stereo buffering requires SGI extensions to OpenGL.
* STR_BOT display mode: The display window should either fill or be entirely contained within the
lower half (492 pixel rows) of the normal display. Stereo buffering requires SGI extensions to
OpenGL.
* Square-pixel stereo display mode (if supported): No restrictions compared to non-stereo display.
To set up a full-screen display window that does not have window borders, using GL:
prefposition (0, getgdesc (GD_XPMAX), 0, getgdesc (GD_YPMAX));
winopen ("");
StereoGraphics CrystalEyes Software Development Kit Page 12
6. Stereoscopic Viewporting and Buffering
STR_RECT stereo mode:
In the STR_RECT stereo mode, the program should have a full-screen application window (see the section
above). The left-eye viewport should then be placed at the top half (the upper 492 out of 1024 pixel rows,
which is slightly less than half) of the normal display, and the right-eye viewport should be placed at the
bottom half (the lower 492 out of 1024 pixel rows) of the normal display. GL's viewport() function
enables this. Assuming a full-screen application window, the following specifies a viewport for the left-
eye view:
viewport (0, 1279, 532, 1023);
and the following specifies a viewport for the right-eye view:
viewport (0, 1279, 0, 491);
STR_TOP and STR_BOT stereo modes:
GL's stereo buffering functions do not work with STR_TOP or STR_BOT stereo modes. Buffering for
these stereo modes requires SGI extensions to OpenGL.
Square-pixel stereo:
GL provides stereoscopic buffering functions that work with the square-pixel stereo mode that is
supported by certain high-end SGI computer systems. These stereo buffering functions work similarly to
(and can be used together with) GL's double-buffering functions which enable smooth graphics animation.
To initialize stereoscopic buffering for an existing graphics window, use the GL function
stereobuffer(). Like the commonly used doublebuffer() function, this will not take effect
until gconfig() is called:
stereobuffer ();
gconfig ();
To disable stereo buffering in a graphics window, call monobuffer() followed by gconfig().
Use GL's leftbuffer() and rightbuffer() functions to draw to the left-eye and/or the right-eye
buffer. To draw to the left-eye buffer only (this is the initial default):
leftbuffer (TRUE);
rightbuffer (FALSE);
StereoGraphics CrystalEyes Software Development Kit Page 13
To draw to the right-eye buffer only:
leftbuffer (FALSE);
rightbuffer (TRUE);
To draw to both eye's buffers at the same time:
leftbuffer (TRUE);
rightbuffer (TRUE);
StereoGraphics CrystalEyes Software Development Kit Page 14
7. Stereoscopic Projections
To produce stereoscopic graphics that both appear natural and are mathematically correct, render your
stereo pairs using two asymmetrical-frustum perspective (also called offset perspective) projections whose
projection axes are parallel to each other.
This involves projecting from two slightly different viewpoints in the scene. Naturally, the left eye's
viewpoint should be offset somewhat to the left of the right eye's viewpoint. Since GL's perspective
projection functions are based on the center of projection (the viewpoint) residing at the coordinate origin,
this viewpoint offset is simulated by translating the scene elements in the opposite direction. Thus, you
want to translate the scene to the right for the left-eye projection, and to the left for the right-eye
projection. This pre-projection translation is accomplished by using GL's translate() function just
after the function that performs the perspective projection.
How much separation should there be between the stereoscopic viewpoints? For stereoscopic graphics
that are both compelling and comfortable, it is important to have enough stereoscopic interaxial
separation, but not too much. Generally, you want to have negative parallax (projected display offset that
causes something to appear to pop out of the screen) ranging from 0% to 3% of the width of the display
window, and you want to have positive parallax (projected display offset that causes something to appear
to sink into the screen) ranging from 0% to 3% of the width of the display window. This can usually be
achieved using a moderately wide angle field of view, and a camera separation that is about 5% of the
distance from the viewpoints to the center of interest in the scene being rendered. Consult other
StereoGraphics documentation for more information about stereoscopic aesthetic issues.
It is extremely important to use asymmetrical-frustum projections for generating stereo pairs. If you use
parallel perspective projections without asymmetrical frustums, the result will be uncomfortable graphics
that reside entirely in negative parallax viewing space (everything coming out of the screen). GL provides
two functions for implementing perspective projections, perspective() and window(). Of these,
only window() permits asymmetrical-frustum projections (you may also implement projections by
defining transformation matrices; see other StereoGraphics documentation for more about the appropriate
transformation matrices for stereoscopic asymmetrical-frustum projections).
The frustum of the left-eye perspective projection should take in a slightly wider angle to the right of its
projection axis than to the left of it, and the frustum of the right-eye projection should take in a slightly
wider angle to the left of its projection axis than to the right. Scene elements that reside in plane at which
the two frustums intersect will be rendered at zero parallax. Scene elements at zero parallax will appear to
reside at the display surface, neither popping out of the screen nor sinking into it. Ideally, scene elements
close to the center of interest in the scene should be rendered at or near zero parallax.
Putting all of the above, the viewpoint translation and the asymmetrical-frustum projection, into GL code:
StereoGraphics CrystalEyes Software Development Kit Page 15
Left eye view:
window (-top * aspect + (half_sep * clip_near / distance), top *
aspect + (half_sep * clip_near / distance), -top, top, clip_near,
clip_far);
translate (half_sep, 0.0, 0.0);
Right eye view:
window (-top * aspect - (half_sep * clip_near / distance), top *
aspect - (half_sep * clip_near / distance), -top, top, clip_near,
clip_far);
translate (-half_sep, 0.0, 0.0);
Where:
top = half of the vertical frustum dimension as measured at the frustum's intersection with the near
clipping plane
aspect = the ratio between overall display width and overall display height (full-screen STR_RECT
stereo), or the ratio between window width and window height (square pixel stereo)
half_sep = half of the interaxial separation
clip_near = the distance from the viewpoint to the near clipping plane
distance = the distance from the viewpoint to the center of interest in the scene
clip_far = the distance from the viewpoint to the far clipping plane
Note that, except with square-pixel stereo, stereo projections should be rendered at a vertically squashed
aspect ratio of about 0.48 (=1024 / 492), since the stereo display mode expands the display vertically by
about a factor of two. The code examples above have already taken aspect ratio into consideration.
Many stereoscopic implementations use a rotation method instead of parallel asymmetrical-frustum
projections. With the rotation method, the two viewpoints are offset as they are above. However, rather
than using two asymmetrical-frustum projections with parallel projection axes, standard (symmetrical-
frustum) projections are done, with projection axes that are angled inward. Typically, the projection axes
intersect at or near the center of interest in the scene. The rotation method has the advantage of being
easier for most people to conceptualize. In addition, the rotation method is able to render into both
positive and negative parallax, without requiring an asymmetrical-frustum projection. However,
compared to the methods described in this section, the rotation method has two significant disadvantages:
It is not mathematically correct, and the results will not look as good. The rotation method introduces
distortion and vertical misalignment. As SGI software libraries and hardware acceleration fully support
asymmetrical-frustum perspective projections, there is no good reason to use the rotation method .
StereoGraphics CrystalEyes Software Development Kit Page 16
8. Cursor and Interface Issues
When using the STR_RECT stereoscopic display mode, it is a good idea to limit the cursor to one eye's
viewport of the display. For reasons having to do with pop-up menus, it is generally best to lock the
cursor in the right-eye viewport:
setvaluator (MOUSEY, 492 / 2, 0, 492);
To restore the cursor range:
setvaluator (MOUSEY, 1024 / 2, 0, 1024);
With STR_RECT stereo mode, menus, buttons, dialog boxes, scroll bars, text, and anything else that you
wish to appear for both eyes, must be separately drawn to both eyes' viewports. All such interface
elements will appear vertically stretched compared to how they are drawn, due to the aspect ratio
difference between the viewport and the display.
StereoGraphics CrystalEyes Software Development Kit Page 17
9. Sample GL Application
* This program illustrates how to generate effective and comfortable
* stereoscopic images using the Silicon Graphics GL graphics library.
* This program illustrates:
* * Testing whether the SGI machine being used is stereo-ready
* * Switching system to stereo display mode
* * Setting up for full-screen stereoscopic graphics
* * Stereoscopic offset projections
* * Stereoscopic viewporting
* * Automatic adjustment of stereoscopic separation
* * Restoring system to non-stereo display mode
** This program will work on stereo-ready SGI computer systems which
* normally run at 1280 x 1024 display resolution.
** The program's code is entirely contained in this file.
** To compile this program:
* cc star.c -o star -lgl_s -lm
*/
#include <gl.h>
#include <device.h>
#include <get.h>
#include <math.h>
#define YSTEREO 491
#define YOFFSET_LEFT 532
#define ROT_SPEED 6
#define MOVE_SPEED 0.04
#define MAX_DISTANCE 25.0
#define LEFT_EYE -1
#define RIGHT_EYE 1
#define PI 3.14159
/* function prototypes */
int main (void);
void redraw (float distance, int rotation, float fovy_degrees,
float dim_ratio);
void draw_star (void);
void stereoperspective (float fovy_degrees, float distance, int which_eye,
float znear, float zfar, float aspect);
float determine_stereo_separation (float distance, float tan_of_half_fovx);
/* global variables: */
long xres, yres;
int main (void)
{ int rotation = 0;
short value, mode, monitor;
long dev_id;
float dim_ratio, move_speed, init_position, position;
/* initialize variables */
xres = getgdesc (GD_XPMAX);
yres = getgdesc (GD_YPMAX);
dim_ratio = (float) xres / (float) yres;
move_speed = -MOVE_SPEED;
position = init_position = MAX_DISTANCE;
StereoGraphics CrystalEyes Software Development Kit Page 18
/* open full screen window */
prefposition (0, xres, 0, yres);
winopen ("Star");
/* check to see if system is stereo-ready */
if (getgdesc (GD_STEREO) == 0L) {
printf ("System is not stereo-ready\n");
exit (0);
}
/* save original display mode and switch to stereo */
monitor = (short) getmonitor ();
setmonitor (STR_RECT);
/* limit cursor range to one eye's subfield */
setvaluator (MOUSEY, YSTEREO / 2, 0, YSTEREO);
/* save original matrix mode */
mode = (short) getmmode ();
mmode (MPROJECTION);
/* allow color specification, activate doublebuffering */
RGBmode ();
doublebuffer ();
gconfig ();
/* watch for these 'quit' events */
qdevice (ESCKEY);
qdevice (WINQUIT);
/* clear the screen in both buffers */
frontbuffer (TRUE);
cpack (0L);
clear ();
frontbuffer (FALSE);
/* take user input and drive image until done */
while (TRUE) {
if (qtest ()) {
dev_id = qread (&value);
if (dev_id == ESCKEY || dev_id == WINQUIT)
break;
}
redraw (position, rotation--, 45.0, dim_ratio);
if (rotation < 0)
rotation = 3600 / ROT_SPEED;
position += move_speed;
if (position < 7 || position > MAX_DISTANCE)
move_speed = -move_speed;
}
/* restore to original display, cursor range, matrix mode */
setmonitor (monitor);
setvaluator (MOUSEY, (short) yres / 2, 0, (short) yres);
mmode (mode);
exit (0);
} /* end of main() */
void redraw (float distance, int rotation, float fovy_degrees, float
dim_ratio)
/* called by: main */
{
StereoGraphics CrystalEyes Software Development Kit Page 19
/* draw left eye subfield */
viewport (0, xres, YOFFSET_LEFT, yres);
cpack (0x00111111);
clear ();
stereoperspective (fovy_degrees, distance, LEFT_EYE, 6.0, -6.0,
dim_ratio);
rotate (rotation * ROT_SPEED, 'y');
rotate (-100, 'x');
draw_star ();
/* draw right eye subfield */
viewport (0, xres, 0, YSTEREO);
cpack (0x00111111);
clear ();
stereoperspective (fovy_degrees, distance, RIGHT_EYE, 6.0, -6.0,
dim_ratio);
rotate (rotation * ROT_SPEED, 'y');
rotate (-100, 'x');
draw_star ();
/* display the completed stereo frame */
swapbuffers ();
} /* end of redraw() */
void draw_star (void)
/* called by: redraw */
{ static float vertex[26][3] = {
{-4.0, 0.0, 4.0}, { 4.0, 0.0, 4.0}, { 4.0, 0.0, -4.0},
{-4.0, 0.0, -4.0}, {-4.0, 4.0, 0.0}, { 4.0, 4.0, 0.0},
{ 4.0, -4.0, 0.0}, {-4.0, -4.0, 0.0}, { 0.0, 4.0, 4.0},
{ 0.0, 4.0, -4.0}, { 0.0, -4.0, -4.0}, { 0.0, -4.0, 4.0},
{ 0.0, 0.0, 4.0}, { 4.0, 0.0, 0.0}, { 0.0, 0.0, -4.0},
{-4.0, 0.0, 0.0}, { 0.0, -4.0, 0.0}, { 0.0, 4.0, 0.0},
{-2.0, 2.0, 2.0}, {-2.0, -2.0, 2.0}, { 2.0, 2.0, 2.0},
{ 2.0, -2.0, 2.0}, { 2.0, 2.0, -2.0}, { 2.0, -2.0, -2.0},
{-2.0, 2.0, -2.0}, {-2.0, -2.0, -2.0}
};
cpack (0x00b030ff);
bgnclosedline ();
v3f (vertex[ 0]); v3f (vertex[12]); v3f (vertex[18]); v3f (vertex[17]);
v3f (vertex[20]); v3f (vertex[13]); v3f (vertex[21]); v3f (vertex[ 1]);
v3f (vertex[12]); v3f (vertex[19]); v3f (vertex[15]); v3f (vertex[18]);
v3f (vertex[ 0]); v3f (vertex[15]); v3f (vertex[24]); v3f (vertex[17]);
v3f (vertex[22]); v3f (vertex[ 2]); v3f (vertex[13]); v3f (vertex[ 1]);
v3f (vertex[20]); v3f (vertex[ 5]); v3f (vertex[13]); v3f (vertex[22]);
v3f (vertex[ 5]); v3f (vertex[17]); v3f (vertex[ 9]); v3f (vertex[14]);
v3f (vertex[ 2]); v3f (vertex[23]); v3f (vertex[ 6]); v3f (vertex[13]);
v3f (vertex[23]); v3f (vertex[16]); v3f (vertex[19]); v3f (vertex[11]);
v3f (vertex[12]); v3f (vertex[20]); v3f (vertex[ 8]); v3f (vertex[12]);
v3f (vertex[21]); v3f (vertex[ 6]); v3f (vertex[16]); v3f (vertex[ 7]);
v3f (vertex[15]); v3f (vertex[25]); v3f (vertex[16]); v3f (vertex[21]);
v3f (vertex[11]); v3f (vertex[16]); v3f (vertex[10]); v3f (vertex[14]);
v3f (vertex[22]); v3f (vertex[ 9]); v3f (vertex[24]); v3f (vertex[14]);
v3f (vertex[23]); v3f (vertex[10]); v3f (vertex[25]); v3f (vertex[14]);
v3f (vertex[ 3]); v3f (vertex[15]); v3f (vertex[ 4]); v3f (vertex[17]);
v3f (vertex[ 8]); v3f (vertex[18]); v3f (vertex[ 4]); v3f (vertex[24]);
v3f (vertex[ 3]); v3f (vertex[25]); v3f (vertex[ 7]); v3f (vertex[19]);
endclosedline ();
} /* end of draw_star() */
StereoGraphics CrystalEyes Software Development Kit Page 20
void stereoperspective (float fovy_degrees, float distance, int which_eye,
float znear, float zfar, float aspect)
/*
* This routine does a stereoscopic offset perspective projection for
* one eye's view. The "center of interest" of the scene, which will be
* rendered at a zero parallax setting, is assumed to be at the coordinate
* system origin (0.0, 0.0, 0.0).
** Parameters:
* fovy_degrees = field of view angle in vertical dimension, specified
* in degrees
* distance = the distance from the virtual cameras to the "center of
* interest" of the scene
* which_eye = LEFT_EYE or RIGHT_EYE
* znear, zfar = the near and far clipping plane, relative to the
* "center of interest" of the scene
* aspect = the aspect ratio of the frustrum that will be set up
* appropriate to the dimensions of the viewport
** called by redraw() */
{ float left, right, top, bottom, clip_near, clip_far, camera_separation,
eyeview, horz_tan, vert_tan, fovy_radians, perspective_offset;
clip_near = distance - znear;
clip_far = distance - zfar;
fovy_radians = fovy_degrees * PI / 180.0;
horz_tan = tanf (fovy_radians * aspect / 2.0);
vert_tan = tanf (fovy_radians / 2.0);
camera_separation = determine_stereo_separation (distance, horz_tan);
eyeview = camera_separation / 2.0;
if (which_eye == LEFT_EYE)
eyeview = -eyeview;
top = vert_tan * clip_near;
bottom = -top;
right = horz_tan * clip_near;
left = -right;
perspective_offset = eyeview * (clip_near / distance);
right -= perspective_offset;
left -= perspective_offset;
window (left, right, bottom, top, clip_near, clip_far);
translate (-eyeview, 0.0, -distance);
} /* end of stereoperspective() */
float determine_stereo_separation (float distance, float tan_of_half_fovx)
/*
* This routine estimates an appropriate stereo "virtual camera"
* separation. The wider angle the field of view, and/or the greater the
* distance to the "center of interest" in the 3D scene, the greater the
* camera separation should be for effective and comfortable stereo.
** called by stereoperspective() */
{
return (distance * tan_of_half_fovx / 10.0);
} /* end of determine_stereo_separation() */
StereoGraphics CrystalEyes Software Development Kit Page 21
Chapter 2
SGI OpenGL
1. Stereo Support Under OpenGL
Writing stereoscopic applications for SGI using OpenGL requires using functions in an SGI extension to
OpenGL. These special OpenGL extension functions can be used to develop stereoscopic applications that
are windowed using the STR_TOP or STR_BOT stereoscopic display modes. The SGI OpenGL extension
functions allow you to check whether the display hardware is stereo-ready, switch in and out of
stereoscopic display mode, and draw to either or both of the two eyes' buffers. Detailed information
relating to the SGI OpenGL extensions is beyond the scope of this document.
Asymmetrical-frustum perspective projections may be done in OpenGL using the standard OpenGL
functions glFrustum() and glTranslate(), instead of the similar GL functions window() and
translate().
OpenGL supports stereo via quad buffering. The programmer needs to choose a visual that supports
stereo. Then they draw to the left front/back buffer (you designate left buffer using glDrawBuffer and
then set the xform appropriately before drawing the left-eye view) and to the right front/back buffer. If
drawing to back buffers then glXSwapBuffers must be called after rendering.
In RealityEngine and InfiniteReality, stereo is supported in a window, so the programmer can simply
find a visual that supports stereo and then create a window with that visual. On Impact you need to call
setmon in order to put the gfx in a state where it can support stereo. (If you don't call setmon then you
won't be able to find any visuals that support stereo). On EXPRESS machines (XZ, EXTREME, XL,
Elan) you need to call setmon and then restart the X server.
StereoGraphics CrystalEyes Software Development Kit Page 22
2. Sample OpenGL Application
/*
** A simple OpenGL stereo application. Uses the GLw Motif DrawingArea
** widget with a popup menu. Includes a simple API for controlling
** stereo.
**
** cc -o glwstereo glwstereo.c -lGLw -lXm -lXt -lGL -lXext -lX11 -lm
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/PushBG.h>
#include <Xm/SeparatoG.h>
#include <X11/GLw/GLwMDrawA.h>
/********************************************************************/
#include <X11/extensions/SGIStereo.h>
static struct stereoStateRec {
Bool useSGIStereo;
Display *currentDisplay;
Window currentWindow;
GLXContext currentContext;
GLenum currentDrawBuffer;
int currentStereoBuffer;
Bool enabled;
char *stereoCommand;
char *restoreCommand;
} stereo;
/* call instead of glDrawBuffer */
void
stereoDrawBuffer(GLenum mode)
{ if (stereo.useSGIStereo) {
stereo.currentDrawBuffer = mode;
switch (mode) {
case GL_FRONT:
case GL_BACK:
case GL_FRONT_AND_BACK:
/*
** Simultaneous drawing to both left and right **
buffers isn't really possible if we don't have a ** stereo
capable visual. For now just fall through and use the left buffer.
*/
case GL_LEFT:
case GL_FRONT_LEFT:
case GL_BACK_LEFT:
stereo.currentStereoBuffer = STEREO_BUFFER_LEFT;
break;
case GL_RIGHT:
case GL_FRONT_RIGHT:
stereo.currentStereoBuffer = STEREO_BUFFER_RIGHT;
mode = GL_FRONT;
StereoGraphics CrystalEyes Software Development Kit Page 23
break;
case GL_BACK_RIGHT:
stereo.currentStereoBuffer = STEREO_BUFFER_RIGHT;
mode = GL_BACK;
break;
default:
break;
}
if (stereo.currentDisplay && stereo.currentWindow) {
glXWaitGL(); /* sync with GL command stream before calling X */
XSGISetStereoBuffer(stereo.currentDisplay,
stereo.currentWindow,
stereo.currentStereoBuffer);
glXWaitX(); /* sync with X command stream before calling GL */
}
}
glDrawBuffer(mode);
}
/* call instead of glClear */
void
stereoClear(GLbitfield mask)
{ if (stereo.useSGIStereo) {
GLenum drawBuffer = stereo.currentDrawBuffer;
switch (drawBuffer) {
case GL_FRONT:
stereoDrawBuffer(GL_FRONT_RIGHT);
glClear(mask);
stereoDrawBuffer(drawBuffer);
break;
case GL_BACK:
stereoDrawBuffer(GL_BACK_RIGHT);
glClear(mask);
stereoDrawBuffer(drawBuffer);
break;
case GL_FRONT_AND_BACK:
stereoDrawBuffer(GL_RIGHT);
glClear(mask);
stereoDrawBuffer(drawBuffer);
break;
case GL_LEFT:
case GL_FRONT_LEFT:
case GL_BACK_LEFT:
case GL_RIGHT:
case GL_FRONT_RIGHT:
case GL_BACK_RIGHT:
default:
break;
}
}
glClear(mask);
}
/* call after glXMakeCurrent */
void
stereoMakeCurrent(Display *dpy, Window win, GLXContext ctx)
{ if (stereo.useSGIStereo) {
if (dpy && (dpy != stereo.currentDisplay)) {
int event, error;
/* Make sure new Display supports SGIStereo */
if (XSGIStereoQueryExtension(dpy, &event, &error) == False)
{ dpy = NULL;
}
}
StereoGraphics CrystalEyes Software Development Kit Page 24
if (dpy && win && (win != stereo.currentWindow)) {
/* Make sure new Window supports SGIStereo */
if (XSGIQueryStereoMode(dpy, win) == X_STEREO_UNSUPPORTED) {
win = None;
}
}
if (ctx && (ctx != stereo.currentContext)) {
GLint drawBuffer;
glGetIntegerv(GL_DRAW_BUFFER, &drawBuffer);
stereoDrawBuffer((GLenum) drawBuffer);
}
stereo.currentDisplay = dpy;
stereo.currentWindow = win;
stereo.currentContext = ctx;
}
}
/* call to turn on stereo viewing */
void
stereoEnable(void)
{ if (!stereo.enabled && stereo.stereoCommand) {
system(stereo.stereoCommand);
}
stereo.enabled = True;
}
/* call to turn off stereo viewing */
void
stereoDisable(void)
{ if (stereo.enabled && stereo.restoreCommand) {
system(stereo.restoreCommand);
}
stereo.enabled = False;
}
/* call before using stereo */
void
stereoInit(GLboolean usingStereoVisual, char *stereoCmd, char
*restoreCmd)
{ stereo.useSGIStereo = !usingStereoVisual;
stereo.currentDisplay = NULL;
stereo.currentWindow = None;
stereo.currentContext = NULL;
stereo.currentDrawBuffer = GL_NONE;
stereo.currentStereoBuffer = STEREO_BUFFER_NONE;
stereo.enabled = False;
if (stereo.stereoCommand) {
free(stereo.stereoCommand);
}
stereo.stereoCommand = stereoCmd ? strdup(stereoCmd) : NULL;
if (stereo.restoreCommand) {
free(stereo.restoreCommand);
}
stereo.restoreCommand = restoreCmd ? strdup(restoreCmd) : NULL;
}
/* call when done using stereo */
void
stereoDone(void)
{ stereoDisable();
stereoInit(True, NULL, NULL);
}
StereoGraphics CrystalEyes Software Development Kit Page 25
/************************************************************************/
#define APP_CLASS "GLWApp"
String fallbackResources[] = {
/*
** Widget resources
*/
"*sgiMode: True",
"*useSchemes: all",
"*title: Simple OpenGL Stereo Demo",
"*form.width: 300",
"*form.height: 300",
/*
** Non-Widget (application) resources
*/
"*stereoCommand: /usr/gfx/setmon -n 640x512_120s",
"*restoreCommand: /usr/gfx/setmon -n 72HZ",
"*SGIStereoCommand: /usr/gfx/setmon -n STR_BOT",
"*SGIRestoreCommand: /usr/gfx/setmon -n 60HZ",
NULL,
};
struct appResourceRec {
_XtString stereoCommand;
_XtString restoreCommand;
_XtString SGIStereoCommand;
_XtString SGIRestoreCommand;
} appResources;
XtResource appResourceList[] = {
{ "stereoCommand",
XtCString, XtRString, sizeof (_XtString),
XtOffsetOf(struct appResourceRec, stereoCommand),
XtRImmediate, (XtPointer) NULL },
{ "restoreCommand",
XtCString, XtRString, sizeof (_XtString),
XtOffsetOf(struct appResourceRec, restoreCommand),
XtRImmediate, (XtPointer) NULL },
{ "SGIStereoCommand",
XtCString, XtRString, sizeof (_XtString),
XtOffsetOf(struct appResourceRec, SGIStereoCommand),
XtRImmediate, (XtPointer) NULL },
{ "SGIRestoreCommand",
XtCString, XtRString, sizeof (_XtString),
XtOffsetOf(struct appResourceRec, SGIRestoreCommand),
XtRImmediate, (XtPointer) NULL },
};
XtAppContext appContext;
XtWorkProcId appWorkProcId;
Widget topLevel, form, glw, popup;
GLXContext ctx;
GLboolean animationEnabled = GL_FALSE;
GLboolean stereoEnabled = GL_FALSE;
double lastTime = 0.0;
double rotationRate = 360.0 / 8.0;
double rotation = 0.0;
static void
drawCylinder(void)
{ int numSides = 20;
double radius = 0.7;
double stepSize = 2.0*M_PI / numSides;
int i;
StereoGraphics CrystalEyes Software Development Kit Page 26
glBegin(GL_TRIANGLE_STRIP);
for (i=0; i<=numSides; ++i) {
double a = i * stepSize;
GLfloat x = cos(a);
GLfloat y = sin(a);
glNormal3f(x, y, 0.0);
glVertex3f(x * radius, y * radius, 0.7);
glNormal3f(x, y, 0.0);
glVertex3f(x * radius, y * radius, -0.7);
}
glEnd();
}
static void
initialize(void)
{ GLfloat lightAmb[4] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat lightPos[4] = { 1.5, 1.5, 1.5, 1.0 };
GLfloat matAmbDiff[4] = { 0.30, 0.40, 0.80, 1.0 };
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -2);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matAmbDiff);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
void
stereoFrustum(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top,
GLfloat near, GLfloat far, GLfloat eyeDist, GLfloat
eyeOffset)
{ GLfloat eyeShift = (eyeDist - near) * (eyeOffset / eyeDist);
glFrustum(left+eyeShift, right+eyeShift, bottom, top, near, far);
glTranslatef(-eyeShift, 0.0, 0.0);
}
static void
redraw(void)
{ GLfloat eyeDist = 2.0;
GLfloat eyeOffset = 0.05;
glPushMatrix();
glRotated(rotation, 0, 1, 0);
if (stereoEnabled) {
/*
** Draw right-eye view.
*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
stereoFrustum(-1, 1, -1, 1, 1, 3, eyeDist, eyeOffset);
glMatrixMode(GL_MODELVIEW);
stereoDrawBuffer(GL_BACK_RIGHT);
stereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawCylinder();
StereoGraphics CrystalEyes Software Development Kit Page 27
} else {
eyeOffset = 0.0;
}
/*
** Draw left-eye view.
*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
stereoFrustum(-1, 1, -1, 1, 1, 3, eyeDist, -eyeOffset);
glMatrixMode(GL_MODELVIEW);
stereoDrawBuffer(GL_BACK_LEFT);
stereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawCylinder();
glPopMatrix();
GLwDrawingAreaSwapBuffers(glw);
}
double
getTime(void)
{ struct timeval time;
gettimeofday(&time);
return ((double) time.tv_sec + (double) time.tv_usec * 1E-6);
}
static Boolean
animateWP(XtPointer clientData)
{ double currentTime = getTime();
double elapsed = currentTime - lastTime;
lastTime = currentTime;
rotation += rotationRate * ((elapsed <= 1.0) ? elapsed : 1.0);
if (rotation >= 360) rotation -= 360;
redraw();
return False;
}
static void
popupCB(Widget w, XtPointer clientData, XtPointer callData)
{ int button = (int) clientData;
Bool needsRedraw = False;
switch (button) {
case 0:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
needsRedraw = True;
break;
case 1:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
needsRedraw = True;
break;
case 2:
animationEnabled = !animationEnabled;
if (animationEnabled) {
lastTime = getTime();
appWorkProcId = XtAppAddWorkProc(appContext, animateWP,
NULL);
} else {
XtRemoveWorkProc(appWorkProcId);
}
StereoGraphics CrystalEyes Software Development Kit Page 28
break;
case 3:
stereoEnabled = !stereoEnabled;
if (stereoEnabled) {
stereoEnable();
} else {
stereoDisable();
}
needsRedraw = True;
break;
case 4:
stereoDone();
exit(EXIT_SUCCESS);
break;
default:
break;
}
if (needsRedraw) {
redraw();
}
}
static void
popupEH(Widget w, XtPointer clientData, XEvent *event, Boolean *cont)
{ Widget popup = *((Widget *) clientData);
if ((event->type == ButtonPress) && (event->xbutton.button ==
Button3)) {
XmMenuPosition(popup, &event->xbutton);
XtManageChild(popup);
}
}
static void
exposeCB(Widget w, XtPointer clientData, XtPointer callData)
{ redraw();
}
static void
resizeCB(Widget w, XtPointer clientData, XtPointer callData)
{ GLwDrawingAreaCallbackStruct *glwcallData =
(GLwDrawingAreaCallbackStruct *) callData;
glViewport(0, 0, glwcallData->width, glwcallData->height);
redraw();
}
int
main(int argc, char **argv)
{ int stereoAttrs[] = {
GLX_STEREO,
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 1,
GLX_DEPTH_SIZE, 1,
None,
};
int visualAttrs[] = {
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 1,
GLX_DEPTH_SIZE, 1,
StereoGraphics CrystalEyes Software Development Kit Page 29
None,
};
Display *dpy;
int scrn;
XVisualInfo *vis;
Arg args[10];
int n;
topLevel = XtOpenApplication(&appContext, APP_CLASS, NULL, 0,
&argc, argv, fallbackResources,
applicationShellWidgetClass, NULL, 0);
XtGetApplicationResources(topLevel, &appResources, appResourceList,
XtNumber(appResourceList), NULL, 0);
dpy = XtDisplay(topLevel);
scrn = XScreenNumberOfScreen(XtScreen(topLevel));
if ((vis = glXChooseVisual(dpy, scrn, stereoAttrs)) != NULL) {
/* initialize for use with a stereo capable visual */
stereoInit(True,
appResources.stereoCommand,
appResources.restoreCommand);
} else if ((vis = glXChooseVisual(dpy, scrn, visualAttrs)) != NULL)
{ /* initialize for use without a stereo capable visual */
stereoInit(False,
appResources.SGIStereoCommand,
appResources.SGIRestoreCommand);
/* Force the topLevel widget to maintain a 2:1 aspect ratio */
n = 0;
XtSetArg(args[n], XmNminAspectX, 2); n++;
XtSetArg(args[n], XmNminAspectY, 1); n++;
XtSetArg(args[n], XmNmaxAspectX, 2); n++;
XtSetArg(args[n], XmNmaxAspectY, 1); n++;
XtSetValues(topLevel, args, n);
} else {
fprintf(stderr, "can't find appropriate visual\n");
exit(EXIT_FAILURE);
}
if ((ctx = glXCreateContext(dpy, vis, 0, True)) == NULL) {
fprintf(stderr, "can't create rendering context\n");
exit(EXIT_FAILURE);
}
form = XtCreateManagedWidget("form", xmFormWidgetClass, topLevel,
NULL, 0);
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], GLwNvisualInfo, vis); n++;
glw = XtCreateManagedWidget("glw", glwMDrawingAreaWidgetClass,
form, args, n);
XtAddCallback(glw, GLwNexposeCallback, exposeCB, 0);
XtAddCallback(glw, GLwNresizeCallback, resizeCB, 0);
{
XmButtonType buttonTypes[] = {
XmPUSHBUTTON, XmPUSHBUTTON, XmSEPARATOR, XmPUSHBUTTON,
XmSEPARATOR, XmPUSHBUTTON, XmSEPARATOR, XmPUSHBUTTON,
StereoGraphics CrystalEyes Software Development Kit Page 30
};
XmString buttonLabels[XtNumber(buttonTypes)];
buttonLabels[0] = XmStringCreateLocalized("draw filled");
buttonLabels[1] = XmStringCreateLocalized("draw lines");
buttonLabels[2] = NULL;
buttonLabels[3] = XmStringCreateLocalized("toggle animation");
buttonLabels[4] = NULL;
buttonLabels[5] = XmStringCreateLocalized("toggle stereo mode");
buttonLabels[6] = NULL;
buttonLabels[7] = XmStringCreateLocalized("quit");
n = 0;
XtSetArg(args[n], XmNbuttonCount, XtNumber(buttonTypes)); n++;
XtSetArg(args[n], XmNbuttonType, buttonTypes); n++;
XtSetArg(args[n], XmNbuttons, buttonLabels); n++;
XtSetArg(args[n], XmNsimpleCallback, popupCB); n++;
popup = XmCreateSimplePopupMenu(form, "popup", args, n);
XtAddEventHandler(form, ButtonPressMask, False, popupEH,
&popup);
XmStringFree(buttonLabels[0]);
XmStringFree(buttonLabels[1]);
XmStringFree(buttonLabels[3]);
XmStringFree(buttonLabels[5]);
XmStringFree(buttonLabels[7]);
}
XtRealizeWidget(topLevel);
GLwDrawingAreaMakeCurrent(glw, ctx);
stereoMakeCurrent(dpy, XtWindow(glw), ctx);
initialize();
XtAppMainLoop(appContext);
StereoGraphics CrystalEyes Software Development Kit Page 31
Chapter 3
SUN
1. Introduction
All Creator3D and ZX graphics devices include built-in support for StereoGraphics CrystalEyes (R)
compatible stereoscopy. All monitors shipped with these graphics devices are multi-scan units designed to
handle the higher vertical frequency that CrystalEyes requires.
This document discusses methods for developing stereoscopic software on Sun systems using XGL,
including:
* Checking Stereoscopic Hardware
* Configuring the Frame Buffer for Stereo Display Mode
* Rendering to the Left and Right Eyes
* Two Dimensional Rendering
* Stereoscopic Projections
* Sample XGL Application
StereoGraphics CrystalEyes Software Development Kit Page 32
2. Checking Stereoscopic Hardware
This section discusses how an application should check the stereoscopic compatibility of a user's
computer system. All Sun Creator3D and ZX graphics devices with multi-scan monitors are Stereo
capable. The graphics devices that do not support Stereo are the GX series, the SX, and the GS. If you
would like to test your own computer's stereo compatibility, the xgl_inquire call (see section 4) may
be used within the program to determine if the graphics window has hardware support for Stereo. This
should be taken into consideration if the application enables Stereo via the user interface. If there is no
hardware support for Stereo, the application should not allow XGL_WIN_RAS_STEREO_MODE to be set
to anything but XGL_STEREO_NONE.
StereoGraphics CrystalEyes Software Development Kit Page 33
3. Configuring the Frame Buffer for Stereo Display Mode
The frame buffer must be reconfigured to support stereoscopic display. This is done with the ffbconfig
command on the Creator3D and the leoconfig command on the ZX. Only root may run these commands
and it must be done outside of the windowing system. In CDE, choose "Command Line Login" from the
"Options" option menu on the login screen.
To configure the Creator3D for stereo, issue the following command:
ffbconfig -res 960x680x108s
or:
ffbconfig -res 960x680x112s
To configure the ZX for stereo, issue the following command:
leoconfig -M stereo108
or:
leoconfig -M stereo114
After the reconfiguration of the Frame Buffer is complete, the user may login. All windows will now
appear larger due to the decreased resolution of the screen. This will not affect the functionality of the
window manager and the screen may be viewed with or without the CrystalEyes glasses with no adverse
effects.
To return the Creator3D frame buffer to the standard default, use:
ffbconfig -res 1280x1024x76
To return the ZX frame buffer to the standard default, use:
leoconfig -M 1280_76
See the man pages for ffbconfig and leoconfig for more detail.
StereoGraphics CrystalEyes Software Development Kit Page 34
4. Rendering the Left and Right Eyes
To render the left eye view in a stereo window in XGL, the following attribute is specified in an
xgl_object_set call:
xgl_object_set (ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_LEFT,
NULL);
For the right eye, it would be:
xgl_object_set (ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_RIGHT,
NULL);
And to turn stereo off:
xgl_object_set (ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_NONE,
NULL);
These calls must be made before any rendering takes place in the window. For stereo viewing, the image is
shifted to one side and then rendered for the left eye, then shifted in the opposite direction and rendered
again for the right eye. When stereo is off, the image is rendered only once with no extra shifting taking
place.
Since the Frame Buffer is configured for Stereo, there is no special window to create to enable Stereo.
All rendering is done in a standard graphics window.
StereoGraphics CrystalEyes Software Development Kit Page 35
5. Two Dimensional Rendering
Any two dimensional primitives that are to be rendered in the graphics window should be drawn either
before or after the left and right eye renderings have taken place and stereo has been turned off.
StereoGraphics CrystalEyes Software Development Kit Page 36
6. Stereoscopic Projections
XGL does not have an API function to control asymmetric frustum projections.
The programmer must implement appropriate image re-positioning when using default symmetric frustum
projections to render stereo image pairs.
Refer to Chapter 6 of the "StereoGraphics Developer's Handbook" for mathematical description of
creating stereoscopic projections.
StereoGraphics CrystalEyes Software Development Kit Page 37
7. Sample XGL Application
This program allows stereo image pairs to be viewed on the ZX using the Crystal Eyes glasses. The
program is written in "C" and uses XGL for the stereo and imaging and xview for the windows.
The syntax for the program is:
stereo lefteyefile righteyefile
ex: stereo left.ras right.ras
Included is the source (stereo.c) and makefile (make stereo). A pair of sample stereo images (left.ras,
right.ras) are available from the StereoGraphics website at www.stereographics.com.
The source draws some of the code from leotool. The sample images were created by doing raster saves
from leotool.
/*
***********************************************************************
*
* stereo.c
*
* Program to load in stereo image pairs on the ZX and display them
* in stereo using the crystal eyes glasses.
*
***********************************************************************
*/
#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <xview/xview.h>
#include <xview/canvas.h>
#include <xview/xv_xrect.h>
#include <stdio.h>
#include <xgl/xgl.h>
/* not sure what these are - they were in leotool so I'll keep them */
#define V_STEREO_WIDTH_IN_PIXELS 960.0
#define V_STEREO_HEIGHT_IN_PIXELS 680.0
#define V_STEREO_WIDTH_IN_CM 36.5
#define V_STEREO_HEIGHT_IN_CM 25.8
#define V_EYE_POS_X (V_STEREO_WIDTH_IN_CM / 2.0)
/*
* Definitions from rasterfile.h
*/
struct rasterfile {
int ras_magic; /* magic number */
int ras_width; /* width (pixels) of image */
int ras_height; /* height (pixels) of image */
int ras_depth; /* depth (1, 8, or 24 bits) of pixel */
int ras_length; /* length (bytes) of image */
int ras_type; /* type of file; see RT_* below */
int ras_maptype; /* type of colormap; see RMT_* below */
int ras_maplength; /* length (bytes) of following map */
StereoGraphics CrystalEyes Software Development Kit Page 38
/* color map follows for ras_maplength bytes, followed by image */
};
#define RAS_MAGIC 0x59a66a95
/* Sun supported ras_type's */
#define RT_OLD 0 /* Raw pixrect image in 68000 byte order */
#define RT_STANDARD 1 /* Raw pixrect image in 68000 byte order */
#define RT_BYTE_ENCODED 2 /* Run-length compression of bytes */
#define RT_FORMAT_RGB 3 /* XRGB or RGB instead of XBGR or BGR */
#define RT_FORMAT_TIFF 4 /* tiff <-> standard rasterfile */
#define RT_FORMAT_IFF 5 /* iff (TAAC format) <-> standard rasterfile */
#define RT_EXPERIMENTAL 0xffff /* Reserved for testing */
#define RMT_RAW 2 /* Sun supported ras_maptype's */
#define RMT_NONE 0 /* ras_maplength is expected to be 0 */
#define RMT_EQUAL_RGB 1 /* red[ras_maplength/3],green[],blue[] */
static Xgl_3d_ctx ctx;
static Xgl_ras ras = NULL; /* Xgl window raster */
static Xgl_win_ras win_ras = NULL; /* Xgl window raster */
static Xgl_boolean raster_bug_fixed;
static Xgl_sys_state sys_st; /* XGL System State object */
typedef struct xg_raster_type xg_raster_type;
/* Raster values */
struct xg_raster_type {
/* Offset into original image */
int left_offset;
int top_offset;
/* Size taken from original image */
int width;
int height;
/* Where to draw it in the window */
int x_origin;
int y_origin;
/* Specifications for the stored image */
unsigned * image_data;
int image_width;
int image_height;
};xg_raster_type xg_raster;
char re_name[50],le_name[50];
static void image_proc();
main (argc, argv)
int argc;
char *argv[];
{ Xgl_X_window xgl_x_win; /* XGL-X data structure */
Xgl_obj_desc win_desc; /* XGL window raster structure */
Frame frame; /* XView frame around this window */
Canvas canvas; /* XView canvas inside frame */
Xv_Window pw; /* XView paint window */
Window frame_window; /* XID of frame */
Window canvas_window; /* XID of canvas */
Display *display; /* pointer to X display */
int screen; /* X screen number */
void event_proc (); /* XView event procedure */
void repaint_proc (); /* XView repaint procedure */
Notify_value quit_proc (); /* XView quit procedure */
StereoGraphics CrystalEyes Software Development Kit Page 39
/* this is a leotool thing - not sure what it involves, but if
there is ever a fix, we can turn off the kludge */
if (getenv("LEOTOOL_RASTER_BUG_FIXED")) {
raster_bug_fixed = TRUE;
}
if(argc<3) {
printf("usage: stereo lefteyefile righteyefile\n");
exit(0);
}
else {
strcpy(re_name,argv[1]);
strcpy(le_name,argv[2]);
}
xv_init (XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);
/* create a XView simple window frame */
frame = xv_create (NULL, FRAME,
FRAME_LABEL, "Stereo Image Viewer",
WIN_DYNAMIC_VISUAL, TRUE,
OPENWIN_AUTO_CLEAR, FALSE,
CANVAS_RETAINED, FALSE,
CANVAS_FIXED_IMAGE, FALSE,
WIN_DEPTH, 24,
XV_VISUAL_CLASS, TrueColor,
NULL);
/* create a XView canvas. set event and repaint procedures */
canvas = xv_create (frame, CANVAS,
WIN_DYNAMIC_VISUAL, TRUE,
OPENWIN_AUTO_CLEAR, FALSE,
CANVAS_RETAINED, FALSE,
WIN_EVENT_PROC, event_proc,
CANVAS_REPAINT_PROC, repaint_proc,
WIN_DEPTH, 24,
XV_VISUAL_CLASS, TrueColor,
NULL);
/* get X stuff */
display = (Display *) xv_get (frame, XV_DISPLAY);
pw = (Xv_Window) canvas_paint_window (canvas);
canvas_window = (Window) xv_get (pw, XV_XID);
frame_window = (Window) xv_get (frame, XV_XID);
/* put X stuff into XGL data structure */
xgl_x_win.X_display = (void *) XV_DISPLAY_FROM_WINDOW (pw);
xgl_x_win.X_window = (Xgl_usgn32) canvas_window;
xgl_x_win.X_screen = (int) DefaultScreen (display);
/* wait for the window */
sleep (2);
/* create XGL System State object */
sys_st = xgl_open (NULL);
/* create Window Raster Device using XView canvas */
win_desc.win_ras.type = XGL_WIN_X | XGL_WIN_X_PROTO_DEFAULT;
win_desc.win_ras.desc = &xgl_x_win;
win_ras = (Xgl_win_ras) xgl_object_create(sys_st,
XGL_WIN_RAS, &win_desc,
StereoGraphics CrystalEyes Software Development Kit Page 40
XGL_DEV_COLOR_TYPE, XGL_COLOR_RGB,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_NONE,
NULL);
ras = (Xgl_ras) xgl_object_create(sys_st,
XGL_MEM_RAS, NULL,
XGL_RAS_DEPTH, 32,
XGL_RAS_WIDTH, 5, /* It WILL change */
XGL_RAS_HEIGHT, 5,
NULL);
/* create XGL graphics Context object using the Window Raster object */
ctx = xgl_object_create (sys_st, XGL_3D_CTX, NULL,
XGL_CTX_DEVICE, win_ras,
XGL_CTX_DEFERRAL_MODE, XGL_DEFER_ASAP,
NULL);
/* set quit procedure where xgl_close is called */
notify_interpose_destroy_func (frame, quit_proc);
image_proc();
/* go into XView loop */
window_main_loop (frame);
exit (0);
}
/*
* Load up the left and right eye in this function.
*/
static
void
image_proc ()
{ Xgl_bounds_i2d rect; /* Size of rectangle to be drawn */
Xgl_pt_i2d pos; /* Position on screen */
int read_succeeded; /* True if object read worked */
/* clear canvas area (both eyes) to default background color of black */
xgl_object_set(win_ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_NONE,
NULL);
xgl_context_new_frame (ctx);
rect.xmin = xg_raster.left_offset;
rect.xmax = xg_raster.left_offset + xg_raster.width;
rect.ymin = xg_raster.top_offset;
rect.ymax = xg_raster.top_offset + xg_raster.height;
pos.x = xg_raster.x_origin - (xg_raster.width / 2);
pos.y = xg_raster.y_origin - (xg_raster.height / 2);
if (!raster_bug_fixed) {
if (pos.x < 0) {
rect.xmin -= pos.x;
pos.x = 0;
}rect.xmin *= 4; /* Correct for Leo XGL bug */
if (pos.y < 0) {
rect.ymin -= pos.y;
pos.y = 0;
}
}
StereoGraphics CrystalEyes Software Development Kit Page 41
/* Draw the left eye view */
xgl_object_set(win_ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_LEFT,
NULL);
read_succeeded = load_raster_file(le_name);
xgl_context_copy_buffer(ctx, &rect, &pos, ras);
/* Draw the right eye view */
xgl_object_set(win_ras,
XGL_WIN_RAS_STEREO_MODE, XGL_STEREO_RIGHT,
NULL);
read_succeeded = load_raster_file(re_name);
xgl_context_copy_buffer(ctx, &rect, &pos, ras);
}
static
void
event_proc (window, event, arg)
Xv_Window window;
Event *event;
Notify_arg arg;
{ switch (event_action (event)) {
/*
* not anything to do here but mouse and keyboard events should be
* tracked in this procedure
*/
default:
break;
}
}
/*
* Xview repaint procedure. Load up the left and right eye in this function.
*/
static
void
repaint_proc (local_canvas, local_pw, dpy, xwin, xrects)
Canvas local_canvas;
Xv_Window local_pw;
Display *dpy;
Window xwin;
Xv_xrectlist *xrects;
{ image_proc();
}
static
Notify_value
quit_proc (fr, status)
Frame fr;
Destroy_status status;
{ if (status == DESTROY_CHECKING) {
xgl_close (sys_st);
}
return (notify_next_destroy_func (fr, status));
}
/*
StereoGraphics CrystalEyes Software Development Kit Page 42
* load_raster_file
*
* Load in a Sun Rasterfile.
*
* Note that this code is designed to be extended so that
* color maps can be handled better. If the need arises,
* we just have to update the color map code, not anything else.
*/
int
load_raster_file(char filename[])
{ FILE * input; /* The input file */
struct rasterfile rf; /* File header */
unsigned char red[256]; /* For the CLUT, if present */
unsigned char green[256];
unsigned char blue[256];
int x, y; /* Index into frame buffer array */
unsigned * p; /* Pointer to pixel array */
unsigned char * bptr; /* Pointer TO image input buffer */
unsigned char * iptr; /* Pointer INTO image input buffer */
unsigned char r, g, b; /* To build a 32-bit word */
unsigned c; /* The finished color value */
int i; /* Iterative counter */
input = fopen(filename, "r");
if (input == NULL) {
fprintf(stderr, "Error opening file...\n");
perror(filename);
return (FALSE);
}
/* Read in the header and check for any possible error */
if (fread(&rf, sizeof(struct rasterfile), 1, input) != 1) {
fprintf(stderr, "Unable to read rasterfile header\n");
close(input);
return (FALSE);
}
if (rf.ras_magic != RAS_MAGIC) {
fprintf(stderr, "Invalid magic number: 0x%.8X\n", rf.ras_magic);
close(input);
return (FALSE);
}
if ((rf.ras_depth != 8) && (rf.ras_depth != 24) && (rf.ras_depth != 32)) {
fprintf(stderr, "We don't do depths of %d\n", rf.ras_depth);
close(input);
return (FALSE);
}
if ((rf.ras_width > 4096) || (rf.ras_height > 4096)) {
fprintf(stderr, "Image of %d x %d is too big\n", rf.ras_width,
rf.ras_height);
close(input);
return (FALSE);
}
if ((rf.ras_type != RT_STANDARD) && (rf.ras_type != RT_FORMAT_RGB)) {
fprintf(stderr, "Unsupported ras_type: %d\n", rf.ras_type);
close(input);
return (FALSE);
}
/* Get any color lookup table */
StereoGraphics CrystalEyes Software Development Kit Page 43
for (i = 0; i < 256; i++) { /* First initialize the CLUT */
red[i] = i;
green[i] = i;
blue[i] = i;
}
if (rf.ras_maplength != 0) {
if ((fread(red, rf.ras_maplength / 3, 1, input) != 1) ||
(fread(green, rf.ras_maplength / 3, 1, input) != 1) ||
(fread(blue, rf.ras_maplength / 3, 1, input) != 1)) {
fprintf(stderr, "Unable to read CLUT\n");
close(input);
return (FALSE);
}
/* Undo gamma for 8-bit images... */
if (rf.ras_depth == 8)
for (i = 0; i < 256; i++) {
red[i] = (int) (255.0 * pow((double) red[i] / 255.0, 2.22));
green[i] = (int) (255.0 * pow((double) green[i] / 255.0, 2.22));
blue[i] = (int) (255.0 * pow((double) blue[i] / 255.0, 2.22));
}
} /* End of CLUT loading */
/* Initialize data storage area */
xg_raster.left_offset = 0;
xg_raster.top_offset = 0;
xg_raster.image_width = rf.ras_width;
xg_raster.image_height = rf.ras_height;
xg_raster.width = xg_raster.image_width;
xg_raster.height = xg_raster.image_height;
xg_raster.x_origin = xg_raster.image_width / 2;
xg_raster.y_origin = xg_raster.image_height / 2;
xgl_object_set(ras,
XGL_RAS_WIDTH, xg_raster.image_width,
XGL_RAS_HEIGHT, xg_raster.image_height,
NULL);
xgl_object_get(ras, XGL_MEM_RAS_IMAGE_BUFFER_ADDR,
(unsigned **) &xg_raster.image_data);
/* Now go read the image */
if (rf.ras_length == (rf.ras_width * rf.ras_height))
/* Correct for certain screwed up files */
rf.ras_length = rf.ras_width * rf.ras_height * (rf.ras_depth / 8) +
(rf.ras_width & 1 ? rf.ras_width : 0);
/* Get room for the full image and read it */
bptr = (unsigned char *) malloc(rf.ras_length);
if (fread(bptr, rf.ras_length, 1, input) != 1) {
fprintf(stderr, "Unable to read full image file\n");
close(input);
return (FALSE);
}
p = xg_raster.image_data; /* Point into pixel array */
iptr = bptr;
if (rf.ras_depth == 8) {
for (y = 0; y < rf.ras_height; y++) {
for (x = 0; x < rf.ras_width; x++) {
c = *iptr++;
StereoGraphics CrystalEyes Software Development Kit Page 44
r = red[c];
g = green[c];
b = blue[c];
c = r | (g << 8) | (b << 16);
*p++ = c;
}
if (rf.ras_width & 1)
iptr++; /* Make up for rasterfile kludge */
}
} /* End of 8-bit rasterfile */
else if (rf.ras_depth == 24) {
for (y = 0; y < rf.ras_height; y++) {
for (x = 0; x < rf.ras_width; x++) {
if (rf.ras_type == RT_FORMAT_RGB) {
r = red[*iptr++];
g = green[*iptr++];
b = blue[*iptr++];
}else {
b = blue[*iptr++];
g = green[*iptr++];
r = red[*iptr++];
}c = r | (g << 8) | (b << 16);
*p++ = c;
}
if (rf.ras_width & 1)
iptr++; /* Make up for rasterfile kludge */
}
} /* End of 24-bit rasterfile */
else if (rf.ras_depth == 32) {
for (y = 0; y < rf.ras_height; y++) {
for (x = 0; x < rf.ras_width; x++) {
iptr++; /* Bypass the spare byte */
if (rf.ras_type == RT_FORMAT_RGB) {
r = red[*iptr++];
g = green[*iptr++];
b = blue[*iptr++];
}else {
b = blue[*iptr++];
g = green[*iptr++];
r = red[*iptr++];
}c = r | (g << 8) | (b << 16);
*p++ = c;
}
}
} /* End of 32-bit rasterfile */
free(bptr);
close(input);
return (TRUE);
} /* End of load_raster_file */
StereoGraphics CrystalEyes Software Development Kit Page 45
## @(#)Makefile 1.4 93/02/18
## Copyright (c) 1993 by Sun Microsystems, Inc.
all: stereo
OS:sh = /bin/expr uname -r : '\(.\)'
CC_5 = cc
CC = $(CC_$(OS))
BUILD=
UG_DEBUG_5-g = -g
UG_DEBUG = $(UG_DEBUG_$(OS)$(BUILD))
COMPILE_FLAGS_5 = -c $(UG_DEBUG) -D__SVR4__ -DDEBUG -Dsun -Dsun2 \
-I. -I$(XGLHOME)/include \
-I$(OPENWINHOME)/include
COMPILE_FLAGS = $(COMPILE_FLAGS_$(OS))
.SUFFIXES: .c .o
OTHER_SYSLIBS_5 = -L$(XGLHOME)/lib -lxgl -L/usr/ucblib \
-L$(OPENWINHOME)/lib -lX11 -lXt -ldga -ldl -lintl -lnsl -lsocket \
-lxview -lolgx -lw -lm -lelf
OTHER_SYSLIBS = $(OTHER_SYSLIBS_$(OS))
obj_$(OS)$(BUILD)/%.o: %.c
$(CC) $(COMPILE_FLAGS) -o $@ $<
PWX_SOURCE= \
stereo.c
PWX_OBJECTS_5-g = $(PWX_SOURCE:%.c=obj_5-g/%.o)
PWX_OBJECTS = $(PWX_OBJECTS_$(OS)$(BUILD))
LINK_5_-g =
LINK = $(LINK_$(OS)$(BUILD))
stereo: obj_$(OS)$(BUILD)/stereo.o $(PWX_OBJECTS)
$(CC) $(UG_DEBUG) -o stereo$(BUILD) \
obj_$(OS)$(BUILD)/stereo.o $(PWX_OBJECTS) \
$(LINK) $(OTHER_SYSLIBS)
stereo-g:
make "BUILD=-g" stereo
StereoGraphics CrystalEyes Software Development Kit Page 46
StereoGraphics CrystalEyes Software Development Kit Page 47
Chapter 4
DEC
1. Introduction
This document is intended to give a brief overview of the stereo implementation that is available for the PowerStorm 4DT
family of graphics adapters.
In some cases, information in a section of this document may apply only to OpenGL implementations layered on a
particular operating system (Digital Unix or Windows NT). In such cases, the text is preceded by a bracketed
[ ] disclaimer. OpenGL stereo as described in this document is currently only supported on Digital Unix.
This document does not discuss algorithms for generating stereo views from a 3D scene, and it does not discuss how to
write a stereo application using OpenGL. (Although an example of a simple stereo application is given at the end of this
document). The OpenGL stereo implementation for the PowerStorm 4DT family was intended to be as transparent as
possible, and thus take advantage of pre-existing (and well-documented) API's.
This document is intended to provide developers with preliminary information about unreleased Digital products. As such, it is intended to be as accurate as possible, but may be subject to change without
notice, and should not be construed as a commitment by Digital Equipment Corporation.
This document discusses methods for developing stereoscopic software on DEC systems, including:
* Stereo Support
* Enabling Stereo From an Application
* Supported Screen Resolutions and Refresh Rates
* Supported Hardware
* Supported Visuals
* Backing Store
* Performance
* Left and Right Construction Planes
* Example OpenGL Application
StereoGraphics CrystalEyes Software Development Kit Page 48
2. Stereo Support
Stereo support for the Powerstorm 4DT family of graphics adapters conforms to the model defined by the OpenGL
standard. An application can render a stereoscopic scene by simply drawing to the appropriate left or right buffers. The
hardware mechanics of rendering in stereo mode are completely transparent to the applications developer.
The form of stereo rendering that has been implemented is commonly referred to as 'stereo-in-a-window'. A stereo scene
can be rendered into a normal window on the screen. Pixels that are displayed in a stereo window will have a normal
aspect ratio.
Other monoscopic applications can displayed on the same screen, and will appear normal. Multiple stereo windows and
monoscopic windows can be displayed simultaneously, can overlap and can be managed by a standard window manager.
StereoGraphics CrystalEyes Software Development Kit Page 49
3. Enabling Stereo from an Application
To enable stereo rendering, an application must select a visual or pixel format that supports stereo.
For an OpenGL implementation layered on the X Window System, simply use glXChooseVisual() to select an
appropriate stereo visual. Use glXCreateContext() to create a new GLX rendering context using the visual that was
selected. When the application calls glXMakeCurrent() with this context, the screen will be switched into stereo mode,
and rendering can proceed as normal.
The same effect can be achieved using a GL toolkit that supports stereo. For example, when using the OpenGL Utility
Toolkit (GLUT), simply OR in the GLUT_STEREO bit to the mode argument when calling glutInitDisplayMode().
Selecting the left or right stereo buffer is accomplished through the standard call to glDrawBuffer(). When the context or
window used for stereo rendering is destroyed, the screen is switched back out of stereo mode. If multiple contexts are
being used for stereo rendering, the screen is not switched out of stereo mode until the last of these contexts is destroyed.
An application can determine if a particular display is stereo- capable by calling glXGetConfig() with the requested
attribute set to GLX_STEREO.
StereoGraphics CrystalEyes Software Development Kit Page 50
4. Supported Screen Resolutions and Refresh Rates
In order to support normal aspect ratio pixels in stereo mode, the frame buffer is divided into two full height left and right
buffers.
Because of the increased memory requirements associated with maintaining separate left and right buffers, the maximum
screen resolution for each graphics adapter will be slightly smaller than in non-stereo mode.
Maximum Stereo Resolutions
PowerStorm Model Planes Per Pixel Max Resolution
4D60T 102 1280x1024
128 1152x900
4D50T/4D40T 102 800x600
128 800x600
[NOTE: the following information applies only to OpenGL implementations layered on the X Window System.]
To select a particular screen resolution and vertical refresh rate, use the '-screen' and '-vsync' command line options in the
X server configuration file.
To select an additional vertical refresh rate to be used when the screen is in stereo mode, use the '-I -e3stereo ' command
line arguments in the Xserver configuration file. Replace by one of the vertical refresh rates in the table below. Note that
the maximum refresh rates indicated may not be supported by all hardware.
If you omit the '-I -e3stereo ' arguments in the Xserver configuration file, the server will use continue use the same rate
that it was using when the screen was in monoscopic mode.
See your system administrator or refer to your documentation and release notes for more information on configuring the
Xserver via the configuration file.
The following table indicates the range of vertical refresh rates available at each of the above resolutions. Note that these
refresh rates are per frame , so in stereo mode, divide by two to get the effective refresh rate per eye . In the comment
column, 'Default' indicates the default refresh rate for monoscopic operation, 'StereoDefault' indicates the default refresh
rate for stereo operation.
Supported Video Modes
Resolution (HxV) Refresh Rate (Hz) Comment
1600x1280 60
1600x1280 65
1600x1280 70 Default
1600x1280 75
1600x1200 60
1600x1200 65
1600x1200 70
Resolution (HxV) Refresh Rate (Hz) Comment
1600x1200 75 Default
1600x1200 80
1600x1200 85
1280x1024 60
1280x1024 65
1280x1024 66
StereoGraphics CrystalEyes Software Development Kit Page 51
1280x1024 70
1280x1024 72
1280x1024 75 Default
1280x1024 80
1280x1024 85 StereoDefault
1152x900 60
1152x900 65
1152x900 70
1152x900 75
1152x900 76 Default
1152x900 80
1152x900 85
1152x900 90
1152x900 95
1152x900 100
1152x900 105
1152x900 110
1024x768 60
1024x768 65
1024x768 70
1024x768 75 Default
1024x768 80
1024x768 85
1024x768 90
1024x768 95
1024x768 100
1024x768 105
1024x768 110 StereoDefault
1024x768 115
1024x864 60 Default
1024x864 65
1024x864 70
1024x864 75
1024x864 80
1024x864 85
Resolution (HxV) Refresh Rate (Hz) Comment
1024x864 90
1024x864 95
1024x864 100
1024x864 105
1024x864 110
1024x864 115
1024x864 120
1024x864 125
1024x864 130
1024x864 135
1024x864 140
800x600 60
StereoGraphics CrystalEyes Software Development Kit Page 52
800x600 65
800x600 70
800x600 72
800x600 75 Default
800x600 80
800x600 85
800x600 90
800x600 95
800x600 100
800x600 105
800x600 110
800x600 115
800x600 120
800x600 125
800x600 130
800x600 135
800x600 140 StereoDefault
640x480 60
640x480 65
640x480 70
640x480 72
640x480 75 Default
640x480 80
640x480 85
640x480 90
640x480 95
640x480 100
640x480 105
640x480 110
Resolution (HxV) Refresh Rate (Hz) Comment
640x480 115
640x480 120
640x480 125
640x480 130
640x480 135
640x480 140
640x480 145
640x480 150 StereoDefault
StereoGraphics CrystalEyes Software Development Kit Page 53
5. Supported Hardware
This stereo implementation requires a PowerStorm 4D40T, 4D50T, or 4D60T graphics adapter and compatible MultiSync
monitor, such as Digital's VRC15, VRC17, or VRC21 series monitors.
StereoGraphics CrystalEyes Software Development Kit Page 54
6. Supported Visuals
[NOTE: the following information applies only to OpenGL implementations layered on the X Window System.]
GL stereo is currently supported only on single and double buffered TrueColor-24 visuals. Future support will
include PseudoColor and DirectColor visuals.
StereoGraphics CrystalEyes Software Development Kit Page 55
7. Backing Store
[NOTE: the following information applies only to OpenGL implementations layered on the X Window System.]
Backing store is currently not supported when rendering in stereo mode. Windows that have their backing store attribute
set will have backing store turned off while the screen is in stereo mode and will receive exposure events.
When the screen returns to monoscopic mode, these windows will have backing store re-enabled. Backing store for any obscured
regions will have been lost, but will be available for subsequent window operations.
StereoGraphics CrystalEyes Software Development Kit Page 56
8. Performance
When the graphics adapter is in stereo mode, the rendering performance of monoscopic applications will be lower than
normal. Because of this, it is recommended that when stereoscopic rendering is no longer required, the adapter be
switched out of stereo mode.
This reduction in performance is most apparent in applications that draw large, simple primitives. Large rectangle fills are
the best example of this. Rendering complex three-dimensional scenes with many polygons or textured polygons should
not suffer significant performance degradation.
Switching between normal and stereo modes is fairly expensive, so switching back to normal mode is only required when it is
unlikely that stereo rendering will be performed in the near future. An application that renders in both mono and stereo should
remain in stereo mode for as long as stereo rendering is required.
StereoGraphics CrystalEyes Software Development Kit Page 57
9. Left and Right Construction Planes
[NOTE: the following information applies only to OpenGL implementations layered on the X Window System.]
As described above, in this stereo implementation, the frame buffer is divided into separate full-height left and right
buffers. This means that when the screen is in stereo mode, the left and right buffers use separate portions of the frame
buffer for the construction planes. (Depth buffer, alpha buffer, and stencil buffer).
This does not cause problems in normal rendering, but it can cause unexpected results when an application reads and
writes to these buffers. OpenGL implicitly assumes that there is only one of each of these construction buffers.
In the case of an application that reads from these planes (via glReadPixels), data will always be fetched from the buffer
associated with the left stereo buffer. When an application writes to these buffers (via DrawPixels) the data is replicated
and sent to both the left and right buffers.
Note that having separate depth, stencil, and alpha buffers for the left and right stereo buffers will not cause problems for
monoscopic applications that are running when the screen is in stereo mode. For these applications, the same information is
rendered in both the left and right buffers, so both sets of construction planes will be identical.
StereoGraphics CrystalEyes Software Development Kit Page 58
10. Sample OpenGL Application
Demonstration of a simple stereo application in OpenGL and GLUT. This application puts a crude stereo image of a
paper airplane on the screen, rotating about its center.
This program uses perspective depth, which draws things in the distance smaller. The only correct way to do this in gl is
with the "window" subroutine made to perform asymmetric projection by the use of parallel cameras. It is incorrect to
angle the cameras inward so that their lines of gaze intersect at a point in space.
For orthographic display (no perspective -- things in the distance drawn the same size as things up close) it is correct to
angle the cameras inward. The gl "perspective" may be used in this case if one wishes.
The original version of the program before modification, was by Robert Akka published in The CrystalEyes Handbook,
previously distributed by StereoGraphics.
/*
*
* plane.c (OpenGL version)
* Draws rotating paper airplane in stereo.
*
* Converted to use OpenGL Utility Toolkit
* Rick Hammerstone, Digital, December 1996.
*
* Converted to OpenGL by Silicon Graphics Corp. 4Dgifts program toogl.
* Further modified by James S. Lipscomb and Keh-Shin Cheng, IBM Research,
* March 1995. Uses Motif.
*
* Original program in GL by Robert Akka
* StereoGraphics Corporation
* April 2, 1991
*
* Compile with:
* cc -o plane plane.c -lglut -lGLU -GL -lXmu -lXi -lXext -lX11 -lm
*
* Hit escape to stop program.
*/
#include stdio.h
#include string.h
#include math.h
#include GL/glut.h
/*
* Speed of rotation in degrees per update.
*/
#define VELOCITY -0.2
float yAngle = 0;
GLenum rgb, doubleBuffer;
void Reshape(int width, int height)
{ glViewport(0, 0, width, height);
}
void Key(unsigned char key, int x, int y)
{ if (key == 27)
exit(0);
}
void Init()
StereoGraphics CrystalEyes Software Development Kit Page 59
{ glMatrixMode(GL_PROJECTION);
}
void DrawAirplane()
{ static float airplane[9][3] = {
{ 0.0, 0.5, -4.5},
{ 3.0, 0.5, -4.5},
{ 3.0, 0.5, -3.5},
{ 0.0, 0.5, 0.5},
{ 0.0, 0.5, 3.25},
{ 0.0, -0.5, 5.5},
{-3.0, 0.5, -3.5},
{-3.0, 0.5, -4.5},
{ 0.0, -0.5, -4.5}
};
glColor3f(1.00, 0.19, 0.69); /* violet: r=1, g=.19, b=.69 */
glBegin(GL_LINE_LOOP);
glVertex3fv(airplane[6]);
glVertex3fv(airplane[7]);
glVertex3fv(airplane[1]);
glVertex3fv(airplane[2]);
glVertex3fv(airplane[4]);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3fv(airplane[0]);
glVertex3fv(airplane[4]);
glVertex3fv(airplane[5]);
glVertex3fv(airplane[8]);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3fv(airplane[6]);
glVertex3fv(airplane[3]);
glVertex3fv(airplane[2]);
glEnd();
}
void Plane(float yAngle)
{ glRotatef(yAngle, 0, 1, 0);
glRotatef(-10, 1, 0, 0);
DrawAirplane();
}
void StereoProjection(float left,
float right,
float bottom,
float top,
float near,
float far,
float zero_plane,
float dist,
float eye)
/* Perform the perspective projection for one eye's subfield.
The projection is in the direction of the negative z axis.
-6.0, 6.0, -4.8, 4.8,
left, right, bottom, top = the coordinate range, in the plane of zero
parallax setting, which will be displayed on the screen. The
ratio between (right-left) and (top-bottom) should equal the aspect
ratio of the display.
StereoGraphics CrystalEyes Software Development Kit Page 60
6.0, -6.0,
near, far = the z-coordinate values of the clipping planes.
0.0,
zero_plane = the z-coordinate of the plane of zero parallax setting.
14.5,
dist = the distance from the center of projection to the plane
of zero parallax.
-0.31
eye = half the eye separation; positive for the right eye subfield,
negative for the left eye subfield.
*/
{ float xmid,
ymid,
clip_near,
clip_far,
topw,
bottomw,
leftw,
rightw,
dx,
dy,
n_over_d;
dx = right - left;
dy = top - bottom;
xmid = (right + left) / 2.0;
ymid = (top + bottom) / 2.0;
clip_near = dist + zero_plane - near;
clip_far = dist + zero_plane - far;
n_over_d = clip_near / dist;
topw = n_over_d * dy / 2.0;
bottomw = -topw;
rightw = n_over_d * (dx / 2.0 - eye);
leftw = n_over_d *(-dx / 2.0 - eye);
/* Need to be in projection mode for this. */
glLoadIdentity();
glFrustum(leftw, rightw, bottomw, topw, clip_near, clip_far);
glTranslatef(-xmid - eye, -ymid, -zero_plane - dist);
}
void DrawScene(void)
{ glDrawBuffer(doubleBuffer ? GL_BACK : GL_FRONT);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawBuffer(doubleBuffer ? GL_BACK_LEFT : GL_FRONT_LEFT);
glPushMatrix();
StereoProjection(-6.0, 6.0, -4.8, 4.8, 6.0, -6.0, 0.0, 14.5, -0.31);
Plane(yAngle);
glPopMatrix();
glDrawBuffer(doubleBuffer ? GL_BACK_RIGHT : GL_FRONT_RIGHT);
glPushMatrix();
StereoProjection(-6.0, 6.0, -4.8, 4.8, 6.0, -6.0, 0.0, 14.5, 0.31);
Plane(yAngle);
glPopMatrix();
StereoGraphics CrystalEyes Software Development Kit Page 61
if (doubleBuffer)
glutSwapBuffers();
else
glFlush();
yAngle -= VELOCITY;
if (yAngle < 0)
yAngle = 360.0 + yAngle; /* degrees */
}
GLenum Args(int argc, char **argv)
{ GLint i;
rgb = GL_TRUE;
doubleBuffer = GL_TRUE;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-ci") == 0) {
rgb = GL_FALSE;
} else if (strcmp(argv[i], "-rgb") == 0) {
rgb = GL_TRUE;
} else if (strcmp(argv[i], "-sb") == 0) {
doubleBuffer = GL_FALSE;
} else if (strcmp(argv[i], "-db") == 0) {
doubleBuffer = GL_TRUE;
} else {
printf("%s (Bad option).\n", argv[i]);
return GL_FALSE;
}
}
return GL_TRUE;
}
main(int argc, char **argv)
{ GLenum type;
glutInit(&argc;, argv);
Args(argc, argv);
type = GLUT_STEREO;
type |= (rgb) ? GLUT_RGB : GLUT_INDEX;
type |= (doubleBuffer) ? GLUT_DOUBLE : GLUT_SINGLE;
glutInitDisplayMode(type);
glutInitWindowSize(300, 300);
glutCreateWindow("Airplane");
Init();
glutReshapeFunc(Reshape);
glutKeyboardFunc(Key);
glutIdleFunc(DrawScene);
glutDisplayFunc(DrawScene);
glutMainLoop();
}
Chapter 5
StereoGraphics CrystalEyes Software Development Kit Page 62
PC
1. Introduction
For this section, a brief overview of the above and below format is warranted. The above-below method
uses two subfields arranged above and below each other in a single standard field. The images in these
subfields are squeezed top to bottom by a factor of two.
At the standard 60 fields per second it takes half the duration of an entire field, or 1/120th second, to scan
a subfield. When played back on a monitor operating at 120 fields per second, the subfields which had
been juxtaposed spatially become juxtaposed temporally. Therefore each eye of the beholder, when
wearing the proper shuttering eyewear, will see 60 fields of image per second, out of phase with the other
60 fields prepared for the other eye. Thus it is possible to see a flicker-free stereoscopic image, because
each eye is seeing a pattern of images of 1/120th second followed by 1/120th second of darkness. When
one eye is seeing an image, the other is not, and vice versa. (The field rate is typically 120, but anything
somewhat slower or a great deal faster will work fine for many applications.)
Today there are many models of high-end graphics monitors which will run at field rates of 120 or higher.
Providing a synchronization pulse is added between the subfields in the subfield blanking area, such a
monitor will properly display such images. The monitor can unsqueeze the image in the vertical so the
picture has the normal proportions and aspect ratio.
StereoGraphics' synch doubling emitter, the model EPC, for the above-and-below format is available for
PCs. It adds the missing synchronization pulses to the vertical blanking for a proper video signal. The
EPC unit also displays SimulEyes white-line-code formatted images. The shutters in the eyewear are
triggered by the emitter's infra-red signal.
If the image has high enough resolution to begin with, or - more to the point - enough raster lines, then
the end result is pleasing. Below 300 to 350 lines per field the image starts to look coarse on a good sized
monitor viewed from two feet, and that's about the distance people sit from workstations or PC monitors
- which sheds light on the basis for why this approach is obsolete for video. NTSC has 480 active video
lines. It uses a two-fold interlace so each field has 240 lines. Using the subfield technique, the result is
four 120-line fields for one complete stereoscopic image.
At the frequently used 1280x1024 resolution an above-and-below formatted image will wind up at about
1280x500 pixels per eye (some lines are lost to blanking). Even from the workstation viewing distance of
two feet, most people would agree that this is good quality.
Additional details about the above/below stereoscopic format can be found in Chapter 6 of the
StereoGraphics Developers' Handbook.
StereoGraphics CrystalEyes Software Development Kit Page 63
2. Example Windows NT OpenGL Application
The C code fragment below is adapted from an OpenGL example "TQUAD.c" provided in the WinNT
SDK CD-ROM (from Microsoft Corp.). It demonstrates how to display a stereoscopic image pair using the
above/below format. The complete "TQUAD.c" file is also available from the StereoGraphics web site, as
well as on the floppy included with hard-copies of this document.
Since the above/below format uses the available window client area, no special API extensions need to be
called. This is because the display output is modified externally to the computer, by a device such as
StereoGraphics' Sync-Doubling Emitter.
So the application software renders the left and right stereoscopic views onto the single client area as two
OpenGL viewports. The rendering generates the stereo views in pairs on the same window surface,
regardless of single- or double-buffered hardware operations by the PC graphics controller.
<<<<<<<<<<<<<<<<<<<<<<<
#if STEREO
#define EYE_OFFSET 0.200 // default viewpoint separation
#define EYE_ADJUST -0.070 // default horizontal image shift adjustment
int winWidth, winHeight; // client window
int winAdjust=30; // Y height adjustment to "above" viewport
GLfloat eyeDist=4.5; // Z distance for zero parallax setting
GLfloat eyeOffset=EYE_OFFSET; // X offset for left/right viewpoint separation
GLfloat eyeAdjust=EYE_ADJUST; // X adjustment for horizontal image shift
#endif
static void CALLBACK Reshape(int width, int height)
{#if STEREO
winWidth = width;
winHeight = height;
// viewport and perspective matrixes must be updated every drawn frame
// to render left and right views in above/below format onto same client window
#else
glViewport(0, 0, (GLint)width, (GLint)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1, 1, -1, 1, 1, 10);
gluLookAt(2, 2, 2, 0, 0, 0, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
#endif
}
static void CALLBACK Draw(void)
{#if STEREO
// calculate delta-X translation for left or right perspective view
GLfloat dx0 = eyeAdjust; // asymmetrical viewing pyramid offset
GLfloat dx1 = eyeOffset; // viewpoint separation
// set "above" viewport for left eye rendering
StereoGraphics CrystalEyes Software Development Kit Page 64
glViewport(0, (GLint)(winHeight/2 + winAdjust), (GLint)winWidth,
(GLint)(winHeight/2 - winAdjust));
// set left-eye perspective
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1-dx0, 1-dx0, -1, 1, 1, 10); // skew viewer symmetry leftward
glTranslatef(0+dx1, 0, 0); // move rightward for left eye view
glTranslatef(0, 0, -3); // pull back to look at
glMatrixMode(GL_MODELVIEW);
#endif // STEREO
glLoadIdentity();
glRotatef(xRotation, 1, 0, 0);
glRotatef(yRotation, 0, 1, 0);
glRotatef(zRotation, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
switch (whichQuadric) {
case 0:
glTranslatef(0, 0, -height/20.0);
gluCylinder(quadObj, radius1/10.0, radius2/10.0, height/10.0,
slices, stacks);
break;
case 1:
gluSphere(quadObj, radius1/10.0, slices, stacks);
break;
case 2:
gluPartialDisk(quadObj, radius2/10.0, radius1/10.0, slices,
stacks, angle1, angle2);
break;
case 3:
gluDisk(quadObj, radius2/10.0, radius1/10.0, slices, stacks);
break;
}
# if STEREO
// set "below" viewport for right eye rendering
glViewport(0, 0, (GLint)winWidth, (GLint)(winHeight/2 - winAdjust));
// set right-eye perspective
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1+dx0, 1+dx0, -1, 1, 1, 10); // skew viewer symmetry rightward
glTranslatef(0-dx1, 0, 0); // move leftward for right eye view
glTranslatef(0, 0, -3); // pull back to look at
glMatrixMode(GL_MODELVIEW);
// render scene again for right-eye view
glLoadIdentity();
glRotatef(xRotation, 1, 0, 0);
glRotatef(yRotation, 0, 1, 0);
glRotatef(zRotation, 0, 0, 1);
glColor3f(1.0, 1.0, 1.0);
switch (whichQuadric) {
case 0:
glTranslatef(0, 0, -height/20.0);
gluCylinder(quadObj, radius1/10.0, radius2/10.0, height/10.0,
slices, stacks);
break;
case 1:
StereoGraphics CrystalEyes Software Development Kit Page 65
gluSphere(quadObj, radius1/10.0, slices, stacks);
break;
case 2:
gluPartialDisk(quadObj, radius2/10.0, radius1/10.0, slices,
stacks, angle1, angle2);
break;
case 3:
gluDisk(quadObj, radius2/10.0, radius1/10.0, slices, stacks);
break;
}
#endif // STEREO
glFlush();
if (doubleBuffer) {
auxSwapBuffers();
}
StereoGraphics CrystalEyes Software Development Kit Page 66
3. OpenGL Stereo Quad Buffering Under Windows NT
With Windows NT 4.0 and Windows 95 service release 2, Microsoft has provided a software-only version
of OpenGL with many common rendering functions. 3D graphics accelerator boards include a type of
extension driver (called a mini client driver, MCD, or installable client driver, ICD) which intercepts the
OpenGL API calls, and redirects them to the equivalent hardware functions.
The WIN32 API ChoosePixelFormat() passes a Pixel Format Descriptor for specifying an OpenGL
rendering context via PDF_ALLOW_OPENGL flag. PDF_STEREO flag may be added to
PDF_DOUBLEBUFFER to specify that stereo quad buffering is requested. Subsequent calls to WIN32
APIs wglCreateContext() and wglMakeCurrent() set the stereo display mode, and make GL_STEREO
quad buffers available for drawing via OpenGL APIs.
In the OpenGL SDK for Windows NT, Microsoft provides their own toolkit library, GLAUX, which is
comparable to the GLUT toolkit for X-Windows systems. The GLAUX library source is provided for
compilation in the \OPENGL\GLAUX subdirectory, and ultimately produces GLAUX.LIB for linking
with OpenGL example applications. GLAUX provides the system buffer swap function,
auxSwapBuffers(), which will update stereo pairs if GL_STEREO quad buffering is effective.
GLAUX also provides a high-level initialization API, auxInitDisplayMode(), for making the above
sequence of system calls to ChoosePixelFormat(), etc, for a Windows display context. Since Microsoft did
not include stereo support in NT 4.0's software-only OpenGL library, an AUX_STEREO flag needs to be
assigned to complement AUX_DOUBLEBUFFER. This is done in the modified TK.C toolkit source for
compiling GLAUX.LIB, and utilized in the modified TQUAD.C example.
The modified TQUAD.C example may be used for either above/below format stereo on any graphics card,
or GL_STEREO quad buffering on stereo-ready graphics cards. Below are code fragments for highlighting
stereo quad buffer support. The complete listing of the modified TQUAD.C example file is posted at:
ftp.stereographics.com/developers/tquad.zip
You must have installed the Microsoft OpenGL SDK for Windows NT in order to use these modified files
to build this example.
// at initialization:
// specify a display format to be passed to GLAUX toolkit library
type = AUX_DEPTH16;
type |= (rgb) ? AUX_RGB : AUX_INDEX;
type |= (doubleBuffer) ? AUX_DOUBLE : AUX_SINGLE;
// pass additional STEREO flag to GLAUX toolkit
if (stereoQuadBuffer)
type |= AUX_STEREO;
// enable stereo quad-buffer display modes through GLAUX
auxInitDisplayMode(type);
auxInitWindow("Stereoscopic Viewport Test");
//...
// in callback routine for main event loop:
// calculate delta-X translation for left or right perspective view
GLfloat dx0 = eyeAdjust; // asymmetrical viewing pyramid offset
GLfloat dx1 = eyeOffset; // viewpoint separation
// set back buffer for left eye rendering
StereoGraphics CrystalEyes Software Development Kit Page 67
glDrawBuffer(GL_BACK_LEFT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// set left-eye perspective
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1-dx0, 1-dx0, -1, 1, 1, 10); // skew viewer symmetry leftward
glTranslatef(0+dx1, 0, 0); // move rightward for left eye view
// render objects in scene for left-eye view
glMatrixMode(GL_MODELVIEW);
// ...
glFlush();
// set back buffer for right eye rendering
glDrawBuffer(GL_BACK_RIGHT);
// clear separate draw buffer for right eye rendering
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// set right-eye perspective
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1+dx0, 1+dx0, -1, 1, 1, 10); // skew viewer symmetry rightward
glTranslatef(0-dx1, 0, 0); // move leftward for right eye view
// render scene again for right-eye view
glMatrixMode(GL_MODELVIEW);
// ...
glFlush();
// update stereo pair just rendered
auxSwapBuffers();
StereoGraphics CrystalEyes Software Development Kit Page 68
4. Example OpenGL Application for Diamond Multimedia
Systems' FireGL 4000 Graphics Board
The FireGL 4000 3D graphics board from Diamond Multimedia Systems is equipped with internal sync
doubling circuitry. This provides a convenient technique for supporting stereoscopic applications which
already use the popular "above/below" display format.
Since the sync-doubler is already built-in, no additional accessories are necessary to plug in-between the
graphics board and the monitor connector.
Stereoscopic eyewear plug directly in to the mini-DIN3 stereo sync connector, which has been proposed as
VESA standard. The user has the choice of plugging in the IR emitter for wireless CrystalEyes eyewear, or
the direct-drive wired SimulEyes eyewear.
Additionally, the function of toggling sync-doubled stereoscopic display on and off is under control of
software. Unlike the external sync-doubling emitter accessory, the user does not need to press a manual
button to enable or disable the sync-doubled stereoscopic display. Instead the application can make the
equivalent driver function calls to enable or disable stereoscopic display according to the appropriate
stereoscopic or monoscopic display context.
In order to make such hardware-specific function calls to the stereo-ready FireGL 4000, the application
makes use of the OpenGL QueryExtension API mechanism for searching for extension APIs. The same
scheme is initially used to detect and identify that a FireGL 4000 is installed in the system, so that the
application can make use of the stereoscopic hardware capabilities.
Listed below is a C code fragment utilizing the FireGL 4000's stereoscopic extension APIs for the
preceding OpenGL example "TQUAD.C". The complete example which has been modified and provided
by Diamond Multimedia Systems is also included on the CrystalEyes SDK disk or via the StereoGraphics'
web site.
/************************************************************************
* Copyright (C) 1997 Diamond Multimedia Systems, Inc. *
************************************************************************/
#define DIMD_STEREO_EXTENSION "GL_EXT_dimd_stereo"
#define GLQUERYSTEREOEXTPROC "glQueryStereoEXT"
#define GLSETSTEREOMODEEXTPROC "glSetStereoModeEXT"
#define GLENABLESTEREOEXTPROC "glEnableStereoEXT"
#define GLDISABLESTEREOEXTPROC "glDisableStereoEXT"
#define GLDISPLAYSTEREOSUPPORTEXTPROC "glDisplayStereoSupportEXT"
#define STEREO_QUAD_BUFFER 1 // Currently not supported
#define STEREO_ABOVE_BELOW 2 // Currently supported by FIRE 4000,
// 1280x1024 True Colour 60Hz
typedef struct tagStereoDisplayContext {
GLuint uiSize; // Must be set to sizeof(STEREODISPLAYCONTEXT).
GLuint uiWinWidth; // Width of window (== width of screen) in pixel is returned.
GLuint uiWinHeight; // Height of window (== height of screen) in pixel is returned.
GLuint uiWinAdjust; // Window adjustment between "above" and "below" is returned.
} STEREODISPLAYCONTEXT, *PSTEREODISPLAYCONTEXT;
// Note: All functions return GL_TRUE in case of success.
StereoGraphics CrystalEyes Software Development Kit Page 69
typedef GLboolean (APIENTRY * PFNGLQUERYSTEREOEXTPROC)(GLenum stereoCap);
typedef GLboolean (APIENTRY * PFNGLSETSTEREOMODEEXTPROC)(GLenum stereoMode,
PSTEREODISPLAYCONTEXT stereoDisplayContext);
typedef GLboolean (APIENTRY * PFNGLENABLESTEREOEXTPROC)(void);
typedef GLboolean (APIENTRY * PFNGLDISABLESTEREOEXTPROC)(void);
typedef GLboolean (APIENTRY * PFNGLDISPLAYSTEREOSUPPORTEXTPROC)(GLuint uiWidth, GLuint
uiHeight, GLuint uiBitsPerPixel, GLuint uiVFrequency);
// Example:
//
// ...
// "Create full-screen window without border"
// ...
// Try to enable stereo mode
LPCSTR lpszExtentions = glGetString(GL_EXTENSIONS);
if (strstr(glGetString(GL_EXTENSIONS), DIMD_STEREO_EXTENSION)) {
PFNGLQUERYSTEREOEXTPROC pfnStereoQuery =
(PFNGLQUERYSTEREOEXTPROC)wglGetProcAddress(GLQUERYSTEREOEXTPROC);
PFNGLSETSTEREOMODEEXTPROC pfnStereoSet =
(PFNGLSETSTEREOMODEEXTPROC)wglGetProcAddress(GLSETSTEREOMODEEXTPROC);
PFNGLENABLESTEREOEXTPROC pfnStereoEnable =
(PFNGLENABLESTEREOEXTPROC)wglGetProcAddress(GLENABLESTEREOEXTPROC);
if (pfnStereoQuery && pfnStereoSet && pfnStereoEnable) {
if ((* pfnStereoQuery)(STEREO_ABOVE_BELOW) == GL_TRUE) {
STEREODISPLAYCONTEXT context;
context.uiSize = sizeof(STEREODISPLAYCONTEXT);
if ((* pfnStereoSet)(STEREO_ABOVE_BELOW, &context)) {
winWidth = context.uiWinWidth;
winHeight = context.uiWinHeight;
winAdjust = context.uiWinAdjust;
if ((* pfnStereoEnable)())
stereoEnabled = TRUE;
}
}if (!stereoEnabled) { // Find out on which videomode(s) stereo is
supported.
PFNGLDISPLAYSTEREOSUPPORTEXTPROC pfnDisplayStereoSupport =
(PFNGLDISPLAYSTEREOSUPPORTEXTPROC)wglGetProcAddress(GLDISPLAYSTEREOSUPPORTEXTPROC);
if (pfnDisplayStereoSupport) {
DWORD dwMode = 0;
CHAR szMsg[2048];
lstrcpy(szMsg, "Stereo is only supported in the following
videomode(s):\n");
while (TRUE) {
if (EnumDisplaySettings(NULL, dwMode++, &devMode) == FALSE)
break;
if ((* pfnDisplayStereoSupport)(
devMode.dmPelsWidth,
devMode.dmPelsHeight,
devMode.dmBitsPerPel,
devMode.dmDisplayFrequency)) {
wsprintf(
szMsg + lstrlen(szMsg),
"\t%4dx%4dx%2d, %3d Hz\n",
devMode.dmPelsWidth,
devMode.dmPelsHeight,
devMode.dmBitsPerPel,
devMode.dmDisplayFrequency
);
}
}MessageBox(hWnd, szMsg, "TQUAD", MB_OK | MB_ICONEXCLAMATION);
DestroyWindow(hWnd);
return 1;
}
StereoGraphics CrystalEyes Software Development Kit Page 70
}}
}
if (!stereoEnabled) { // No stereo support at all.
MessageBox(NULL, "Stereo not supported", "TQUAD", MB_OK | MB_ICONEXCLAMATION);
DestroyWindow(hWnd);
return 1;
}
...
// set "above" viewport for left eye rendering
glViewport(0, winHeight / 2 + winAdjust, winWidth, winHeight / 2 - winAdjust);
...
// set "below" viewport for right eye rendering
glViewport(0, 0, winWidth, winHeight / 2 - winAdjust);
...
// Disable stereo mode
if (stereoEnabled) {
PFNGLENABLESTEREOEXTPROC pfnStereoDisable =
(PFNGLDISABLESTEREOEXTPROC)wglGetProcAddress(GLDISABLESTEREOEXTPROC);
if (pfnStereoDisable) {
if ((* pfnStereoDisable)())
stereoEnabled = FALSE;
}
}
...
StereoGraphics CrystalEyes Software Development Kit Page 71
5. Creating Stereoscopic Renderings Using Kinetix 3D Studio
Products and Autodesk Animator Pro
INTRODUCTION
Kinetix 3D Studio products, with Autodesk Animator Pro (any version), can be used to render
stereoscopic 3D images and animations. Unlike conventional planar renderings, stereoscopic 3D images
convey a natural sense of depth. This sense of true depth can be used to better captivate the viewer in a
presentation setting, or to enhance the viewer's ability to perceive 3D information in an analytical setting.
Creating a stereoscopic image, that is compatible with StereoGraphics CrystalEyes stereo viewing
hardware, requires two steps:
1) The creation of a geometrically correct "stereo pair," consisting of a rendering for the left eye, and
a rendering for the right eye; and,
2) A combining of these elements into a single image, viewported according to the requirements of the
StereoGraphics "above/below" stereo format.
In this two-step process, 3D Studio performs the first step of creating the stereo pair, and Animator Pro
does the second step of merging the stereo components.
StereoGraphics CrystalEyes Software Development Kit Page 72
CRYSTALEYES STEREO: HOW IT WORKS
StereoGraphics CrystalEyes liquid crystal eyewear works by shuttering the left and right eye lenses very
rapidly (60 to 72 times per second per eye), so fast that there is no perceptible "flicker." The glasses are
synchronized by an infrared signal, sent by an infrared emitter that is plugged into a StereoGraphics
controller (PC SDE or GDC-3) or a stereo-ready graphics board.
The StereoGraphics controller works by driving the stereo-ready display at double the normal vertical
synchronization rate. It feeds off of the graphics board's sync signal, to drive both the display and the
glasses. Here is the effect of this double sync pulse, as coordinated by StereoGraphics hardware:
Whatever the graphics board sends to the top half of the display will be seen, full-screen, by the left eye;
whatever the graphics board sends to the bottom half of the display will appear, full-screen, by the right
eye.
There is one additional detail. These two "halves" of the display must be separated by a black "blank
interval," of about 40 to 44 pixel rows. This blank interval allows the display's electron beam time to
return to the top of the display.
Thus, to be compatible with StereoGraphics PC hardware, a computer graphics image or animation must
draw a left eye component to the top half of the display, and a right eye component to the bottom half of
the display. Of course, how the result looks depends on what kind of image you send to each eye.
StereoGraphics CrystalEyes Software Development Kit Page 73
STEP 1: MAKING A STEREO PAIR
Before creating stereo pairs of a still picture or an animation, you must produce a high quality "non-stereo"
rendered image or animation. Feel free to use all available tools and techniques that would apply if the
non-stereo rendering was your final goal, with the following exceptions:
* In general, unless you are rendering 24-bit images, avoid gradient backgrounds, spot lighting, and
other effects that might create unequal gradient bands between left and right eye stereo pair components.
The "Dither 256" system option can eliminate mismatched banding, though this will greatly cut animation
performance.
* Generally, stereo images look best if a relatively wide field of view angle, such as 50 degrees, is used.
Other field of view angles can be used, however.
* Camera Roll complicates matters. Keep your Roll adjustment at 0, if you can.
Once you have a rendering that you are happy with, you must clone the camera that the rendering was
based on. Before doing this, enter the 3D Editor, and examine the camera-target vector. Normally, the
length of the camera-target vector does not matter; only its 3D direction (and the camera's 3D position)
matters. Now, we want to position the target at a particular distance, such that objects close to the target
point will appear, stereoscopically, to rest at the surface of the display. This may involve repositioning the
target point either closer or farther from the camera point. You want the target point to rest near the
"center of interest" of the 3D scene.
There is one more thing that also should be done before cloning the camera. Because the stereo pair
elements will need extra width to facilitate alignment, use Camera/Adjust to widen the field of view angle
by about 20%, for example, from 50* to about 60*.
Now that the camera-target distance is meaningful, measure it. To do this, select the camera view as the
active viewport. Then, without touching the mouse, press the 'U' key, immediately followed by the
<Enter> key. Click the Camera menu item to show the camera and target, if it is not already shown. The
active viewport is now a User viewport, and you are looking straight down the camera-target vector. Next,
rotate the viewport 90*, by pressing the <up-arrow> key nine times (you may want to be in block mode
when you do this). Change the extents of the viewport so that the entire camera-target vector is visible.
You should now be looking at the camera-target vector from a direction perpendicular to that vector. You
can now measure it, using Display/Tape/Find and Display/Tape/Move. Make a note of the distance.
Multiply this distance by 0.06. The resulting value is the interaxial separation, the desired separation
between the camera-target vector corresponding to the left eye's view, and the camera-target vector
corresponding to the right eye's view.
Return to the User view down the camera-target vector, by pressing the <down-arrow> key nine times.
You might want to zoom your viewport view now. You are now ready to clone the camera-target vector
into both a left and right camera-target vector. Use Display/Tape/Find and Display/Tape/Move to show
half the interaxial separation value determined above. While holding down both the <ctrl> and <shift>
keys, select the Camera/Move menu item. Use the <tab> key until the horizontal-lock icon shows. Click
the camera, and create a cloned camera-target vector to the left of the originalcamera. Then create another
clone to the right of the original. These cameras should be named appropriately ("LeftCam" and
"RightCam," for example).
StereoGraphics CrystalEyes Software Development Kit Page 74
If you are creating an animation in which the cameras move and/or if you are using a non-zero Camera
Roll value, you will want to go into the Keyframe Animator, and create a Dummy Object, which is created
while looking down the camera-target vector, and which is centered about the target of the original
camera. The Cameras and Targets of both the left and right cameras (but not the original camera) should
be linked to this dummy object. Now, the left and right cameras can be moved as though they were
mounted to a solid bar.
Unfortunately, you cannot link this Dummy Object as a child of the original camera. Thus, in order for the
Dummy Object, with its two cameras attached, to follow the path of the original camera, you must go to
each frame with a Camera and/or Target Key, and move, and reorient, the Dummy Object until the stereo
cameras' paths roughly agree with the original camera's path (there may be a simpler way to do this; let me
know if you find one). In all frames, the left and right cameras must remain horizontally level, relative to
each other (unless non-zero Camera Roll is used, in which case, the cameras must remain at that Roll
angle, relative to each other). You may rotate the Dummy Object about its z axis to level the cameras
properly. The Dummy Object can also be scaled, along one or more axes at once, in order to change
interaxial separation, and/or "Dolly" the cameras.
It is almost time to render for the left and right camera views. But first, we need to change the render
configuration. In order to do this, we must think about how the stereo pair components will be viewported.
The stereo image or animation must be full-screen, since the entire display must be run at a stereo sync
rate. The left eye component will be viewported to the top half of the display, and the right eye component
will be viewported to the bottom half. These two fields will be separated by a "blank interval" of 40
(1024x768 and higher resolutions, typically) to 44 (640x480 resolution, typically) scan lines, which must
be black. Also recall that we are rendering for a 20% wider field of view than we intend to use. Thus, we
want to render, to disk, at a horizontal resolution that is 1.2 times the horizontal resolution of the final
image, and at a vertical resolution that is a bit less than half (you should calculate exactly using the
numbers above) of the final resolution. In addition, an aspect ratio of about 0.5 should be used. This may
change the camera view somewhat; use the Safe Frame option to check camera placement before
rendering.
Finally, render for both left and right cameras. Render to files that are appropriately named, including a
designation for which camera was used (i.e.: SceneL.gif and SceneR.gif). If you are producing an
animation, it would probably be wise to first render individual frames, particularly those with Keys
involving the cameras and/or the Dummy Object holding the cameras.
StereoGraphics CrystalEyes Software Development Kit Page 75
STEP 2: PUTTING THE STEREO COMPONENTS TOGETHER
We now have two stereo pair elements, one with a left eye view, the other with a right eye view. Both of
these elements are bitmap files, each with a vertically squashed aspect ratio, which is appropriate for
putting together stereo images in the StereoGraphics "above/below" format. Using Autodesk Animator
Pro, we can combine these stereo pair elements.
Upon starting Animator Pro, make sure that the resolution is set to agree with the resolution of the image
or animation that you are about to create. Then, open a new full-screen flic. If you are creating an
animation, set Total Frames to agree with the number of frames in the animation that you are about to
make.
To create a single frame image, you will load the two image files as Cels. Load the left camera's rendering
first. As you load the Cel, the image will briefly flash on the display. To position the Cel, you must Paste
it (Cel/Paste). Recall that we rendered a wider scene than we intend to use. In the left camera rendering,
we will want to chop off more of the left edge (about 15% of display resolution) than the right edge (about
5%), and in the right camera rendering, we will want to chop off more of the right edge than the left edge.
Thus, the left camera's Cel should be moved to the left by about 15% of the horizontal resolution, without
any vertical displacement whatsoever. Once this is fixed in place, load the right camera's rendering as a
Cel, and Paste that to the lower half of the display. At this point, it would be helpful to activate stereo
display mode, so that you can interactively perfect the vertical and horizontal alignment. The vertical
alignment must be exact. The horizontal alignment should be set such that some image elements appear to
come out of the display ("negative parallax"), and other image elements appear to go into the display
("positive parallax"). Once the image looks good, store it as a single image file.
To create a stereo animation from two rendered flics, use the Flic/Composite feature. As with the single
frame method described above, composite the left camera's rendering first. Select Overlay Opaque, in
response to the Composite operation's first prompt. Position the first frame appropriately, with no vertical
displacement, and with a left-ward horizontal displacement equal to about 15% of the horizontal
resolution. When prompted about palette, select "Use Incoming Colors." Once this Composite operation
is done, make sure that you are back at the first frame of the flic, and Composite the right camera's
rendering to the bottom half of the display. Activate the stereo display, and position the flic frame so that
vertical alignment is perfect, and horizontal alignment looks good. When prompted about palette, select
"Combine Color Maps." You can now save the combined animation as a single .FLC file.
StereoGraphics CrystalEyes Software Development Kit Page 76
REPEAT UNTIL SUCCESSFUL
As with non-stereoscopic renderings and animations, stereo renderings and animations will rarely be
perfect on the first try. After your first effort, you will likely want to alter the camera positioning, the
interaxial separation, the positive parallax / negative parallax balance, and probably several other things
that are not particular to stereo. With practice, you will become more proficient at the techniques of
producing stereoscopic renderings. In addition, you will develop a sense of stereo aesthetic. With practice
and experimentation, you will be able to produce stunning stereoscopic imagery, with true 3D depth.
StereoGraphics CrystalEyes Software Development Kit Page 77
StereoGraphics CrystalEyes Software Development Kit Page 78
Chapter 6
HP
1. Introduction
This document describes the configuration necessary to support stereo display on a Hewlett-Packard
workstation, including:
* Supported graphics cards
* Supported monitors
* Enabling stereo support
StereoGraphics CrystalEyes Software Development Kit Page 79
2. Supported Graphics Cards
Stereoscopic display is directly supported on Hewlett-Packard VISUALIZE fx4 and fx6 graphics boards.
Stereoscopic display is also supported on VISUALIZE 48-XP graphics cards, though stereo support may
have to be enabled via onboard jumpers. Likewise there are some older Visualize 8 and Visualize 24 cards
that allow stereo, however, stereo display is no longer a supported configuration for these cards. Contact
Hewlett-Packard for specific jumper specifications on these VISUALIZE series graphics boards.
StereoGraphics CrystalEyes Software Development Kit Page 80
3. Supported Monitors
All of the following HP workstation monitors support the vertical rates required for stereo operation, and
will need cables as noted to support this. Host controllers using the 15-pin D output connector must also
be specially configured for stereo operation, which will usually require both selecting a sync-on-green
stereo timing mode AND moving jumpers to enable the stereo sync output.
Monitors not listed below cannot be used in stereo mode due to limited vertical frequency range.
Monitor Use with EVC host Use with 15-pin D host
A4032A/B Use 15-pin to BNC cable assy Use EVC-to-15p stereo breakout,
(17" Color) connect to RGB monitor inputs then 15-pin adapter with to BNC
ONLY; use H or V cable for cable assembly. Sep syncs may be
external stereo hardware. connected at monitor if desired.
A4330A/B Select BNC inputs on monitor Use EVC-to-15p adapter cable
(17" color) and connect to RGB inputs via w/stereo breakout.
15-pin to BNC cable assy.,
using H or V cable for stereo
hardware.
A4033A/B As for A4032A/B above. As for A4032A/B above.
(20" color)
A4331A/B Use 15-pin to 15-pin cable with Use EVC-to-15p adapter cable
(20" color) H&V lines broken out. (avail. w/stereo breakout.
from StereoGraphics)
A4332A/B No stereo solution available * Use EVC-to-EVC video cable with
(21" color) stereo breakout.
* The A4332A/B is intended to support 1600 x 1200, and no graphics controllers with 15-pin D outputs support this
mode. The A4332A/B does support 1280 x 1024 and lower operation, and with proper adapters may be used with 15-pin
D outputs, but there is no such adapter available with a stereo breakout line. The recommended monitor for stereo on
these systems would be the current A4331A/B or the previous A4033A/B, used as described above.
StereoGraphics CrystalEyes Software Development Kit Page 81
4. Enabling Stereo Support
There is a hardware jumper that must be set to enable stereo support. Please refer to the graphics card installation and
configuration guide for the exact location.
The HP VISUALIZE fx4 and fx6 graphics boards support OpenGL, including the GL_STEREO quad
buffering specification. The VISUALIZE 48 and earlier graphics boards only support HP's Starbase
operating system. Stereoscopic display would only be enabled via system setmon command.
The HP implementation of OpenGL includes GLX and GLUT libraries for X-Windows. The application programmer can
request a stereoscopic display mode by adding GLUT_STEREO flag to glutInitDisplayMode(), or the equivalent
sequence of calls with GLX_STEREO flag for glXChooseVisual().
Refer to the GLUT example described in the DEC OpenGL implementation.
StereoGraphics CrystalEyes Software Development Kit Page 82