|
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