stereo geometry in OpenGL

來源:互聯網
上載者:User

 

OpenGL is a powerful cross-platform graphics API which has many benefits - specifically a wide range of hardware and software support and support for quad-buffered stereo rendering.  Quad-buffering is the
ability to render into left and right front and back buffers independently.  The front left and front right buffers displaying the stereo images can be swapped in sync with shutterglasses while the back left and back right buffers are being updated - giving
a smooth stereoscopic display.  It is also relatively easy to learn with a bit of application  - the code below was written inside of 3 months from the time I started programming due in no small part to the many resources available on the web.

When rendering in OpenGL, understanding the geometry behind what you want to achieve is essential.  Toed-in stereo is quick and easy but does have the side effect that a bit of keystone distortion creeps into both left and right views due to the difference
between the rendering plane and the viewing plane.  This is not too much of a problem in central portions of a scene, but becomes significant at the screen edges.  Asymmetric frustum parallel projection (equivalent to lens-shift in photography) corrects for
this keystone distortion and puts the rendering plane and viewing plane in the same orientation.  It is essential that when using the asymmetric frustum technique that the rendering geometry closely matches the geometry of the viewing system.  Failure to match
rendering and viewing geometry results in a distorted image delivered to both eyes and can be more disturbing than the distortion from toed-in stereo.

Paul Bourke has an excellent site with examples of stereoscopic rendering in OpenGL.  If you are interested in creating stereo views in OpenGL, it is worth spending time working out the geometry
of toed-in (which is quite easy but introduces distortion into the viewing system) and asymmetric frustum parallel axis projection for yourself.  Below is my method - familiarity with OpenGL, GLUT and C is assumed and you need to have a graphics card which
is capable of quad-buffering:

---advertisement---


Toed-in stereo


The idea is to use gluLookAt to set the camera position and point it at the middle of the screen from the two eye positions:

//toed-in stereo

float depthZ = -10.0;                                      //depth of the object drawing

double fovy = 45;                                          //field of view in y-axis
double aspect = double(screenwidth)/double(screenheight);  //screen aspect ratio
double nearZ = 3.0;                                        //near clipping plane
double farZ = 30.0;                                       
//far clipping plane
double screenZ = 10.0;                                    
//screen projection plane
double IOD = 0.5;                                          //intraocular distance

void init(void)
{
  glViewport (0, 0, screenwidth, screenheight);            //sets drawing viewport
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(fovy, aspect, nearZ, farZ);              
//sets frustum using gluPerspective
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
GLvoid display(GLvoid)
{
  glDrawBuffer(GL_BACK);                                  
//draw into both back buffers
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      //clear color and depth buffers

  glDrawBuffer(GL_BACK_LEFT);                              //draw into back left buffer
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();                                        //reset modelview matrix
  gluLookAt(-IOD/2,                                        //set camera position  x=-IOD/2
            0.0,                                          
//                     y=0.0
            0.0,                                          
//                     z=0.0
            0.0,                                          
//set camera "look at" x=0.0
            0.0,                                          
//                     y=0.0
            screenZ,                                      
//                     z=screenplane
            0.0,                                          
//set camera up vector x=0.0
            1.0,                                          
//                     y=1.0
            0.0);                                          //                     z=0.0
 
  glPushMatrix();
  {
    glTranslatef(0.0, 0.0, depthZ);                      
 //translate to screenplane
    drawscene();
  }
  glPopMatrix();

  glDrawBuffer(GL_BACK_RIGHT);                            //draw into back right buffer
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();                                        //reset modelview matrix
  gluLookAt(IOD/2, 0.0, 0.0, 0.0, 0.0, screenZ,            //as for left buffer with camera position at:
            0.0, 1.0, 0.0);                                //                     (IOD/2, 0.0, 0.0)

  glPushMatrix();
  {
    glTranslatef(0.0, 0.0, depthZ);                        //translate to screenplane
    drawscene();
  }
  glPopMatrix();
 
  glutSwapBuffers();
}

---advertisement---


Asymmetric frustum parallel axis projection stereo

This is a bit more complex, as one needs to set up an asymmetric frustum first before moving the camera viewpoint.  In OpenGL, the asymmetric frustum is set up with the camera at the (0.0, 0.0, 0.0) position and then needs to be translated by IOD/2 to make
sure that there is no parallax difference at the screen plane depth.  Geometry for the right viewing frustum is depicted below:


To set up an asymmetric frustum, the main thing is deciding how much to shift the frustum by.  This is quite easy as long as we assume that we only want to move the camera by +/- IOD/2 along the X-axis.  From the geometry it is evident that the ratio of frustum
shift to the near clipping plane is equal to the ratio of IOD/2 to the distance from the screenplane.

I decided to use a function call to set up the frustum on initiallization and anytime the viewport is changed:

#define DTR 0.0174532925  //This is 1 Radian. means : PI/180

struct camera
{
    GLdouble leftfrustum;
    GLdouble rightfrustum;
    GLdouble bottomfrustum;
    GLdouble topfrustum;
    GLfloat modeltranslation;
} leftCam, rightCam;

float depthZ = -10.0;                                      //depth of the object drawing

double fovy = 45;                                          //field of view in y-axis
double aspect = double(screenwidth)/double(screenheight);  //screen aspect ratio
double nearZ = 3.0;                                        //near clipping plane
double farZ = 30.0;                                       
//far clipping plane
double screenZ = 10.0;                                    
//screen projection plane
double IOD = 0.5;                                          //intraocular distance

void setFrustum(void)
{
    double top = nearZ*tan(DTR*fovy/2);                    //sets top of frustum based on fovy and near clipping plane
    double right = aspect*top;                            
//sets right of frustum based on aspect ratio
    double frustumshift = (IOD/2)*nearZ/screenZ;

    leftCam.topfrustum = top;
    leftCam.bottomfrustum = -top;
    leftCam.leftfrustum = -right + frustumshift;
    leftCam.rightfrustum = right + frustumshift;
    leftCam.modeltranslation = IOD/2;

    rightCam.topfrustum = top;
    rightCam.bottomfrustum = -top;
    rightCam.leftfrustum = -right - frustumshift;
    rightCam.rightfrustum = right - frustumshift;
    rightCam.modeltranslation = -IOD/2;
}
void init(void)
{
  glViewport (0, 0, screenwidth, screenheight);            //sets drawing viewport
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}
GLvoid reshape(int w, int h)
{
    if (h==0)
    {
        h=1;                                               //prevent divide by 0
    }
    aspect=double(w)/double(h);
    glViewport(0, 0, w, h);
    setFrustum();
}
GLvoid display(GLvoid)
{
  glDrawBuffer(GL_BACK);                                  
//draw into both back buffers
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      //clear color and depth buffers

  glDrawBuffer(GL_BACK_LEFT);                              //draw into back left buffer
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();                                        //reset projection matrix
  glFrustum(leftCam.leftfrustum, leftCam.rightfrustum,    //set left view frustum
            leftCam.bottomfrustum, leftCam.topfrustum,
            nearZ, farZ);
  glTranslatef(leftCam.modeltranslation, 0.0, 0.0);        //translate to cancel parallax
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity);
  glPushMatrix();
  {
    glTranslatef(0.0, 0.0, depthZ);                      
 //translate to screenplane
    drawscene();
  }
  glPopMatrix();

  glDrawBuffer(GL_BACK_RIGHT);                            //draw into back right buffer
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();                                        //reset projection matrix
  glFrustum(rightCam.leftfrustum, rightCam.rightfrustum,  //set left view frustum
            rightCam.bottomfrustum, rightCam.topfrustum,
            nearZ, farZ);
  glTranslatef(rightCam.modeltranslation, 0.0, 0.0);      //translate to cancel parallax
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity);

  glPushMatrix();
  {
    glTranslatef(0.0, 0.0, depthZ);                        //translate to screenplane
    drawscene();
  }
  glPopMatrix();
 
  glutSwapBuffers();
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.