Page 1 of 1

Yet another glOrtho, glFrustum & gluPerspective (OpenGL)

Posted: Sun Dec 02, 2012 9:29 pm
by fips
I've done a little bit of clean-up in my OpenGL projection routines and put the resulting source code here (see below). You might find it handy if you are looking for a replacement for glOrtho, glFrustum or gluPerspective, especially if you've just switched from the fixed to programmable graphics pipeline and found out that these routines are not available any more.

ma_matrix4_projection.h
ma_matrix4_projection.inl

glOrtho() equivalent:

Code: Select all

template <typename T>
inline Matrix4<T> make_ortho(T left, T right, T bottom, T top, T znear, T zfar)
{
    // from OpenGL spec PDF:                                 can be rewritten as:
    // / 2/(r-l)     0        0       -(r+l)/(r-l)  \        / 2/(r-l)     0        0      (r+l)/(l-r)  \
    // |   0      2/(t-b)     0       -(t+b)/(t-b)  |   =>   |   0      2/(t-b)     0      (t+b)/(b-t)  |
    // |   0         0     -2/(f-n)   -(f+n)/(f-n)  |        |   0         0     2/(n-f)   (f+n)/(n-f)  |
    // \   0         0        0             1       /        \   0         0        0           1       /
    // invalid for: l=r, b=t, or n=f

    FS_ASSERT(left != right);
    FS_ASSERT(bottom != top);
    FS_ASSERT(znear != zfar);

    const T p_fn = zfar + znear;
    const T m_nf = znear - zfar; // ~ -m_fn

    const T p_rl = right + left;
    const T m_rl = right - left;
    const T p_tb = top + bottom;
    const T m_tb = top - bottom;

    const T m_lr = -m_rl;
    const T m_bt = -m_tb;

    return Matrix4<T>(
     T(2)/m_rl,    T(0),       T(0),     p_rl/m_lr,
       T(0),     T(2)/m_tb,    T(0),     p_tb/m_bt,
       T(0),       T(0),     T(2)/m_nf,  p_fn/m_nf,
       T(0),       T(0),       T(0),       T(1)
    );
}
glFrustum() equivalent:

Code: Select all

template <typename T> 
inline Matrix4<T> make_frustum(T left, T right, T bottom, T top, T znear, T zfar)
{
    // from OpenGL spec PDF:                                     can be rewritten as:
    // /  2n/(r-l)    0        (r+l)/(r-l)       0      \        /  2n/(r-l)    0        (r+l)/(r-l)       0      \
    // |     0     2n/(t-b)    (t+b)/(t-b)       0      |   =>   |     0     2n/(t-b)    (t+b)/(t-b)       0      |
    // |     0        0       -(f+n)/(f-n)  -2fn/(f-n)  |        |     0        0        (f+n)/(n-f)   2fn/(n-f)  |
    // \     0        0            -1            0      /        \     0        0            -1            0      /
    // invalid for: n<=0, f<=0, l=r, b=t, or n=f

    FS_ASSERT(znear > T(0));
    FS_ASSERT(zfar > T(0));
    FS_ASSERT(left != right);
    FS_ASSERT(bottom != top);
    FS_ASSERT(znear != zfar);

    const T x_2n = znear + znear;
    const T x_2nf = T(2) * znear * zfar;

    const T p_fn = zfar + znear;
    const T m_nf = znear - zfar; // ~ -m_fn

    const T p_rl = right + left;
    const T m_rl = right - left;
    const T p_tb = top + bottom;
    const T m_tb = top - bottom;

    return Matrix4<T>(
     x_2n/m_rl,    T(0),     p_rl/m_rl,    T(0),
       T(0),     x_2n/m_tb,  p_tb/m_tb,    T(0),
       T(0),       T(0),     p_fn/m_nf,  x_2nf/m_nf,
       T(0),       T(0),       T(-1),      T(0)
    );
}
gluPerspective() equivalent:

Code: Select all

template <typename T> 
inline Matrix4<T> make_perspective(T fovy_deg, T aspect, T znear, T zfar)
{
    // from OpenGL spec PDF:                             can be rewritten as:
    // /    c/a    0        0            0       \       /    c/a    0         0           0      \
    // |     0     c        0            0       |  ==>  |     0     c         0           0      |
    // |     0     0   -(f+n)/(f-n)  -2nf/(f+n)  |       |     0     0    (f+n)/(n-f)  2nf/(n-f)  |
    // \     0     0       -1            0       /       \     0     0        -1           0      /

    FS_ASSERT(znear > T(0));
    FS_ASSERT(zfar > T(0));
    FS_ASSERT(aspect != T(0));
    FS_ASSERT(znear != zfar);

    const T half_fovy_rad = T(M_PI) * fovy_deg / T(360);

    const auto si_co = sin_cos(half_fovy_rad);
    const T si = si_co.first;
    const T co = si_co.second;
    FS_ASSERT(si != T(0));

    const T c = co / si; // cotangent
    const T a = aspect;

    const T x_2nf = T(2) * znear * zfar;
    const T p_fn = zfar + znear;
    const T m_nf = znear - zfar;

    return Matrix4<T>(
     c/a,    T(0),     T(0),       T(0),
     T(0),    c,       T(0),       T(0),
     T(0),   T(0),  p_fn/m_nf,  x_2nf/m_nf,
     T(0),   T(0),     T(-1),      T(0)
    );
}