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?
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:
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:
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.