Tuesday, June 29, 2010

Adapt OpenGL projection matrix to show objects behind the projection plane



In OpenGL the projection operation is defined by the projection matrix, and the objects drawn in the screen are inside the rendering volume defined by the left/right, upper/lower and near/far boundaries of a frustum (truncated pyramid). The projection plane is generally the near plane. The function glfrustum generates a projection matrix with these characteristics.


However there is a problem with the projection defined by glfrustum. Virtual objects in front of the near plane are not shown. However we whould like to show them as well.



The problem is that glFrustum produces a projection matrix that implicitly sets the near plane as a clipping one. Fortunately this can be fixed after understanding how OpenGL performs the clipping test (read http://www.songho.ca/opengl/gl_projectionmatrix.html).


Let's see how the 3d homogeneous coordinates of the scene are rendered.
  • First the the scene is transformed (mainly to position all the objects) with the GL_MODELVIEW matrices.
  •  Second the projection matrix is applied, its task is to convert the rendering volume to NDC (Normalized Device Coordinates) shown below. The NDC are still 3d homogeneous coordinates with w != 1. 
  • Third the vectors (x,y,z,w) are divided by w, the resulting vectors (x/w,y/w,z/w) that have coordinates in [-1,1]^3 are rendered everything that's outside the cube is clipped (discarded). The positions of each 3d point in the screen is "practically the same as" by (x/w,y/w) (I'm skipping other 2d transformations), and the z/w coordinates are fed to the z-buffer. 



The glfrustum projection matrix is


[ 2n/(r-l)   0      (r+l)/(r-l)    0       ]
[ 0       2n/(t-b)  (t+b)/(t-b)    0       ]
[ 0          0     -(f+n)/(f-n) -2fn/(f-n) ]
[ 0          0         -1          0       ]



where n,f<0 are the coordinates of the near and far planes, l,r,t,b are respectively the (left,right,top,bottom) coordinates, all the coordinates are with respect to the viewer position (the origin). In order to display objects in front of the projection plane we need to modify the projection matrix. We want points (x,y,z) with near<0, to be shown. These points are clipped by the glfrustum's matrix since [near, far] is mapped to the range [-1,1].


Observe that the first two rows compute the (x,y) coordinates of the points in the screen, but we are not interested in changing them.
The third row maps [near,far] to [-1,1], ideally we want to have a mapping [q,far] to [-1,1] with 0>q >near. It is easy to see that the following matrix performs this map


[ 2n/(r-l)    0      (r+l)/(r-l)    0       ]
[    0     
2n/(t-b)  (t+b)/(t-b)    0       ]

[    0        0     -(f+q)/(f-q) -2fq/(f-q) ]
[    0        0          -1         0       ]

The glfrustum matrix can be corrected with


/* Compute the projection matrix */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum( -1.0 + X , 1.0 + X, /* left, right */
           -1.0 + Y , 1.0 + Y, /* botom,top*/
           ZNEAR_proj - Z,     /* Z near: the projection plane!*/
           ZFAR                /* Z far */ );

/* Recover the current matrix */
GLfloat projectionMatrix[16];
glGetFloatv(GL_PROJECTION_MATRIX,projectionMatrix);
/* Modify the entries */
projectionMatrix[10]=(-ZFAR-ZNEAR_clip)/(ZFAR-ZNEAR_clip);
projectionMatrix[14]=(-2*ZFAR*ZNEAR_clip)/(ZFAR-ZNEAR_clip);
/* Load the modified matrix */
glLoadMatrixf(projectionMatrix);


On projections: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/homogeneous.html

1 comment:

  1. Any choice of near clipping plane is projectively identical since the effective focal length and size of the focal plane will scale together (similar triangles). The clipping planes simply specify the quantization interval on depth, with given minimum and maximum depths. To view the closer objects, you need only set a closer near clipping plane.

    ReplyDelete