Search code examples
c++openglperspectivecameraprojection-matrix

glFrustum isn't creating perspective


I'm trying to create a 3D manipulation program using C++ and openGL. I'm relatively new to openGL so I often have to look up the documentation to find the right function to do what I want. I thought I had a good understanding of orthogonal vs perspective projections (in that glOrtho creates an orthogonal projection where different z-values don't look different and glFrustum creates a perspective projection where z-values that are closer look bigger). However, when I swap out glOrtho and glFrustum in my program, I don't see any difference. I replicated a small program below that shows the effects. For reference, I'm using openGL with freeglut.

#include "GL/freeglut.h"
void initFunc()
{
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1, 1, -1, 1, -1, 1);

}

void displayFunc()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);

    glColor3f(1.0f, 1.0f, 0.0f);
    glLineWidth(1.0f);
    glutWireTeapot(0.3);

    glTranslatef(0, -0.5, -0.5);
    glutWireTeapot(0.3);

    glutSwapBuffers();
}

int main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitWindowSize(600, 600);
    glutInitWindowPosition(0, 0);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutCreateWindow("Teapot Perspective");

    initFunc();

    glutDisplayFunc(displayFunc);

    glutMainLoop();
}

I'm drawing two teapots slightly offset in both the y and z axes. From what I understand, the glOrtho should render the two teapots as identical with only a y offset, whereas the glFrustum should render one of them bigger than the other. However, both of them render the teapots identically.

Am I missing something here? Are there other steps I have to take to properly set up a perspective projection? Or am I misunderstanding how glFrustum works? I've also tried using gluPerspective instead of glFrustum but I can't seem to find the right values to use. I experimented with a FOV of 90, aspect of 1, and various z values but they all either produce no teapot, or a teapot distorted beyond recognition. Furthermore, the gluPerspective appears to have different behavior than a glFrustum call with corresponding parameters. I'm not sure what I'm missing here.


Solution

  • At Orthographic Projection the coordinates in the view space are linearly mapped to clip space coordinates and the clip space coordinates are equal to the normalized device coordinates, because the w component is 1 (for a cartesian input coordinate).
    The values for left, right, bottom, top, near and far define a box. All the geometry which is inside the volume of the box is "visible" on the viewport.

    The Orthographic Projection Matrix, defined by glOrtho is:

    r = right, l = left, b = bottom, t = top, n = near, f = far
    
    x:  2/(r-l)         0               0               0
    y:  0               2/(t-b)         0               0
    z:  0               0               -2/(f-n)        0
    t:  -(r+l)/(r-l)    -(t+b)/(t-b)    -(f+n)/(f-n)    1
    

    At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
    The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).

    A perspective projection matrix can be defined by a frustum (glFrustum).
    The distances left, right, bottom and top, are the distances from the center of the view to the side faces of the frustum, on the near plane. near and far specify the distances to the near and far plane on the frustum.

    r = right, l = left, b = bottom, t = top, n = near, f = far
    
    x:  2*n/(r-l)      0              0                0
    y:  0              2*n/(t-b)      0                0
    z:  (r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1
    t:  0              0              -2*f*n/(f-n)     0
    

    A specification like this from you question:

    glFrustum(-1, 1, -1, 1, -1, 1);
    

    does not define a proper frustum, because the value for the near plane is negative and the value for the far plane is positive.

    If you would check for OpenGL errors (by glGetError), then you would get an INVALID_OPERATION error.

    OpenGL 4.6 API Compatibility Profile Specification; 12.1. FIXED-FUNCTION VERTEX TRANSFORMATIONS; page 501: >

    void Frustum( double l, double r, double b, double t, double n, double f );
    

    the coordinates (l b −n)T and (r t −n)T specify the points on the near clipping plane that are mapped to the lower left and upper right corners of the window, respectively (assuming that the eye is located at (0 0 0)T). f gives the distance from the eye to the far clipping plane.

    Errors An INVALID_VALUE error is generated if n <= 0, f <= 0, l == r, b == t, or n == f.