Getting coordinates for glFrustum

10,088

I hope I got your question right, if not just let me know, but if you're asking "how you should calculate the glFrustum arguments" this is how:

Its actually all about the aspect ratio and the fov. Usually you're going to find information about how to make the perspective matrix using the vertical fov, but some times you'll want to use the horizontal fov. So heres what I came up with, I don't use glFrustum but I think you can simply switch my perspective function with it and it should still work:

//--------------------------------------------------------------------------------
// set a perspective frustum (right hand)
// (left, right, bottom, top, near, far)
//--------------------------------------------------------------------------------
void perspective(float l, float r, float b, float t, float n, float f)
{
    m_projection.identity();

    m_projection[0]  =  2.0f * n / (r - l);
    m_projection[2]  =  (r + l) / (r - l);
    m_projection[5]  =  2.0f * n / (t - b);
    m_projection[6]  =  (t + b) / (t - b);
    m_projection[10] = -(f + n) / (f - n);
    m_projection[11] = -(2.0f * f * n) / (f - n);
    m_projection[14] = -1.0f;
    m_projection[15] =  0.0f;

    update();
}

//--------------------------------------------------------------------------------
// set a symmetric perspective frustum
// ((vertical, degrees) field of view, (width/height) aspect ratio, near, far)
//--------------------------------------------------------------------------------
void perspective_vertical(float fov, float aspect, float front, float back)
{
    fov = DEG_TO_RAD(fov);                      // transform fov from degrees to radians

    float tangent = tanf(fov / 2.0f);               // tangent of half vertical fov
    float height = front * tangent;                 // half height of near plane
    float width = height * aspect;                  // half width of near plane

    perspective(-width, width, -height, height, front, back);
}

//--------------------------------------------------------------------------------
// set a symmetric perspective frustum
// ((horizontal, degrees) field of view, (width/height) aspect ratio, near, far)
//--------------------------------------------------------------------------------
void perspective_horizontal(float fov, float aspect, float front, float back)
{
    fov = DEG_TO_RAD(fov);                      // transform fov from degrees to radians
    fov = 2.0f * atanf(tanf(fov * 0.5f) / aspect);  // transform from horizontal fov to vertical fov

    float tangent = tanf(fov / 2.0f);               // tangent of half vertical fov
    float height = front * tangent;                 // half height of near plane
    float width = height * aspect;                  // half width of near plane

    perspective(-width, width, -height, height, front, back);
}

And the helping macros:

#define PI_OVER_180 0.0174532925199432957692369076849f
#define 180_OVER_PI 57.2957795130823208767981548141f

#define DEG_TO_RAD(x) (x * PI_OVER_180)
#define RAD_TO_DEG(x) (x * 180_OVER_PI)

The code is mostly commented and should make sense without having to further explain it. The parameters should be something along the lines of:

perspective_horizontal(85.0f /* fov of 85 degrees */, (float)width/height, 0.001f /* using near of 3.0f is kinda too much, just don't use 0.0f */, 1000.0f)

If you want to go more in depth and actually see the numbers working you can put some break points or printf it to see how it works. The equivalent of 85 degrees horizontal is about 45 degrees vertical. Also, opengl uses column major, so if you end up using a matrix like this instead of glFrustum make sure you transpose it first.


EDIT (more about the comment below):

Lets take a window that is standard hd -400 pixels: (1920-400) wide and (1080-400) tall. The aspect ratio of a standard hd is 1.77 but -400 pixels version is (1920-400)/(1080-400) = 2.23.

Now calling the perspective_horizontal function with aspect ration of (1920-400)/(1080-400), fov of 85 and putting a breakpoint before the perspective(...) call is going to give us the following variables:

  • fov float 0.778087676
  • aspect float 2.2352941
  • front float 0.00100000005
  • back float 100
  • tangent float 0.40993762
  • height float 0.000409937638
  • width float 0.000916331192

take note that 0.000916331192/0.000409937638 = 2.23529412052 and 0.778087676 radians to degrees = 44.5811399 degrees vertical which is the equivalent of 85 degrees horizontal.

Also calling the perspective_horizontal function with aspect ration of (1920-400)/(1080-400), fov of 105 instead is going to give us the following variables:

  • fov float 1.05568409
  • aspect float 2.2352941
  • front float 0.00100000005
  • back float 100
  • tangent float 0.583021879
  • height float 0.000583021902
  • width float 0.00130322541

take note again that 0.00130322541/0.000583021902 = 2.23529408677 and 1.05568409 radians to degrees = 60.4862429 degrees vertical which is the equivalent of 105 degrees horizontal.

As for the actual perspective matrix, I can't explain you how the formula works, but just imagine that there are magical unicorns in the gpu and if you feed them gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(inputPosition, 1.0f); they're going to make some magic happen and its going to display beautiful things on the screen ;3.


glFrustum is also explained here, here, here, here, here and most importantly here.

Also theres one good explanation here.

Share:
10,088
user1240679
Author by

user1240679

Updated on June 04, 2022

Comments

  • user1240679
    user1240679 almost 2 years

    I just started reading about OpenGL topics, specifically about Viewing for my current needs. I am trying to understand glFrustum for a perspective projection of objects that I have drawn on the screen and as I understand glFrustum would make the farther objects smaller than the nearer objects.
    I am using openFrameworks here for drawing objects such as Image/Box etc. and here's a short example of what I am trying to do:

    openFrameworks draw method

    draw()
    {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1.0, 1.0, -1.0, 1.0, 3.0, 500.0);
    glMatrixMode(GL_MODELVIEW);
    
    sceneImage.draw(0,0,ofGetWidth(), ofGetHeight()); //draw an image on the screen with window's width and dheight 
    }
    

    With the above glFrustum, I am just trying to to clip the image and see a particular portion of the image for an initial test. I am not sure about what those arguments of glFrustum actually are even as I read about glFrustum from above link of Red book. The arguments of course are left, right, bottom, top, near, far, but how is 1.0 or 1.0 being calculated? I was expecting screen coordinates to go in there and tried something like glFrustum(100, 984, 100, 668,3, 500) but nothing appeared on the screen. How are those arguments for glFrustum calculated?