Modifying the Projection Matrix to Perform Oblique Near-Plane Clipping

Eric Lengyel   •   May 28, 2004

[Edit: This topic is covered in much greater detail in Foundations of Game Engine Development, Volume 2: Rendering.]

The following code modifies the OpenGL projection matrix so that the near plane of the standard view frustum is moved so that it coincides with a given arbitrary plane. The far plane is adjusted so that the resulting view frustum has the best shape possible. This code assumes that the original projection matrix is a perspective projection (standard or infinite). The clipPlane parameter must be in camera-space coordinates, and its w-coordinate must be negative (corresponding to the camera being on the negative side of the plane).

For algorithmic details, see “Oblique View Frustum Depth Projection and Clipping”, by Eric Lengyel. This technique is also described in Game Programming Gems 5, Section 2.6.

#include "TSVector4D.h"

inline float sgn(float a)
{
    if (a > 0.0F) return (1.0F);
    if (a < 0.0F) return (-1.0F);
    return (0.0F);
}

void ModifyProjectionMatrix(const Vector4D& clipPlane)
{
    float       matrix[16];
    Vector4D    q;

    // Grab the current projection matrix from OpenGL
    glGetFloatv(GL_PROJECTION_MATRIX, matrix);
    
    // Calculate the clip-space corner point opposite the clipping plane
    // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
    // transform it into camera space by multiplying it
    // by the inverse of the projection matrix
    
    q.x = (sgn(clipPlane.x) + matrix[8]) / matrix[0];
    q.y = (sgn(clipPlane.y) + matrix[9]) / matrix[5];
    q.z = -1.0F;
    q.w = (1.0F + matrix[10]) / matrix[14];
    
    // Calculate the scaled plane vector
    Vector4D c = clipPlane * (2.0F / Dot(clipPlane, q));
    
    // Replace the third row of the projection matrix
    matrix[2] = c.x;
    matrix[6] = c.y;
    matrix[10] = c.z + 1.0F;
    matrix[14] = c.w;

    // Load it back into OpenGL
    glMatrixMode(GL_PROJECTION);
    glLoadMatrix(matrix);
}