Customizing fixed pipeline projection matrix - opengl

I am working with NV Path extension which forces using of fixed pipeline transformations. Now, while for view and model matrices I just roll my own and load those via:
glMatrixPushEXT(GL_MODELVIEW);
glMatrixLoadfEXT(GL_MODELVIEW, value_ptr(viewmodel) );
I have issues with perspective matrix FOV. The fixed method for creation persp matrix is this:
glMatrixLoadIdentityEXT(GL_PROJECTION);
glMatrixFrustumEXT(GL_PROJECTION, -aspect_ratio, aspect_ratio, -1, 1, nearF, farF);
but it doesn't allow passing a custom FOV. So it probably creates some default FOV based on l ,r ,b ,t params of the method above. Can I use:
glMatrixLoadfEXT(GL_PROJECTION, myProjectionMatrix);
to load a custom projection matrix without using glMatrixFrustumEXT?
UPDATE:
As some peopled pointed out, I can load projection matrix the same way I do with model view.But I found that my custom projection never looks the same as when using glMatrixFrustumEXT(). Probably FOV calculation is different. Anybody knows how glMatrixFrustumEXT calculates the FOV?

Using glFrustum or glMatrixFrustumEXT does allow to specify the FOV, and you actually do so. As you define the points on the near plane, and the distance of the near plane, you implicitely define the fov, both vertical and horizontal.
Look at the old gluPerspective function:
gluPersepctive(fov_y, aspect, zNear, zFar)
This would generate a symmetrical frustum with the given vertical FOV of fov_y as follows:
top=tan(fov_y/2.0) * zNear; // assuming fov_y in radians here
bottom=-top;
left=bottom*aspect;
right=-left;
glFrustum(left, right, bottom, top, zNear, zFar);
In your code, you are simply specifying a vertical FOV of 2*atan(1/nearF)

Related

How to know boundaries of the frustum in case of gluPerspective()?

I have been trying to understand the the coordinates of the frustum gluPerspective() creates.
In case of glOrtho we explicitly define the coordinate space.
for example:
glOrtho(left, right,bottom,top,nearVal, farVal);
tells me what my x,y,z boundaries are, and I can conveniently place objects using
glVertex();
But in case of gluPerspective() I get the frustum but I don't know the the limits of x,y,z coordinates so sometimes when I draw the objects, it is not even in the view.
for example, if i define the frustum like
gluPerspective(45.0f, w/h, 0.1f, 100.0f);
and i draw something like :
glBegin(GL_POLYGON);
glVertex3f(30,30,50);
glVertex3f(-80,20,50);
glVertex3f(60,50,50);
glEnd();
where in the scene would it be? where is the origin located?
also, How the arguments of gluLookat() related to the arguments of gluPerspective();
gluLookat() transforms the objects from world coordinates to camera coordinates.
gluPerspective() Applies perspective to the objects.
The origin is (0,0,0). You locate it where your vertices say.
If you want to know the right and top parameters of the frustum use:
top = n * tan((fov*0.5f) * PI / 180.0f);
r = top * (float)w/(float)h;
In your case fov = 45.
gluLookat() and gluPerspective() have different purposes, so their arguments have nothing to do with each other.
You can use instead of gluPerspective():
glFrustum(left,right,bottom,top,nearVal,farVal)

What does the glFrustum function do?

According to MSDN, we are multiplying the current matrix with the perspective matrix. What matrix are we talking about here? Also from MSDN:
"The glFrustum function multiplies the current matrix by this matrix, with the result replacing the current matrix. That is, if M is the current matrix and F is the frustum perspective matrix, then glFrustum replaces M with M • F."
Now, from my current knowledge of grade 12 calculus (in which I am currently), M is a vector and M dot product F returns a scalar, so how can one replace a vector with a scalar?
I'm also not sure what "clipping" planes are and how they can be referenced via one float value.
Please phrase your answer in terms of the parameters, and also conceptually. I'd really appreciate that.
I'm trying to learn openGL via this tutorial: http://www.youtube.com/watch?v=WdGF7Bw6SUg. It is actually not really good because it explains nothing or else it assumes one knows openGL. I'd really appreciate a link to a tutorial otherwise, I really appreciate your time and responses! I'm sure I can struggle through and figure things out.
You misunderstand what it's saying. M is a matrix. M•F therefore is also a matrix. It constructs a perspective matrix. See this article for an explanation of how it is constructed and when you want to use glFrustum() vs. gluPerspective():
glFrustum() and gluPerspective() both produce perspective projection matrices that you can use to transform from eye coordinate space to clip coordinate space. The primary difference between the two is that glFrustum() is more general and allows off-axis projections, while gluPerspective() only produces symmetrical (on-axis) projections. Indeed, you can use glFrustum() to implement gluPerspective().
Clipping planes are planes that cut out sections of the world so they don't have to be rendered. The frustum describes where the planes are in space. Its sides define the view volume.
glFrustum generates a perspective projection matrix.
This matrix maps a portion of the space (the "frustum") to your screen. Many caveats apply (normalized device coordinates, perspective divide, etc), but that's the idea.
The part that puzzles you is a relic of the deprecated OpenGL API. You can safely ignore it, because usually you apply gluFrustum() on an identity projection matrix, so the multiplication mentioned in the doc has no effect.
A perhaps more comprehensible way to do things is the following :
// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Or, for an ortho camera :
//glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // In world coordinates
// Camera matrix
glm::mat4 View = glm::lookAt(
glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
glm::vec3(0,0,0), // and looks at the origin
glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix : an identity matrix (model will be at the origin)
glm::mat4 Model = glm::mat4(1.0f);
// Our ModelViewProjection : multiplication of our 3 matrices
glm::mat4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around
Here, I used glm::perspective (the equivalent of old-style gluPerspective() ), but you can use gluFrustum too.
As for the arguments of the function, they are best explained by this part of the doc :
left bottom - nearVal and right top - nearVal specify the points on
the near clipping plane that are mapped to the lower left and upper
right corners of the window, assuming that the eye is located at (0,
0, 0)
If you're unfamiliar with transformation matrices, I wrote a tutorial here; the "Projection Matrix" section especially should help you.

GLM - Calculating the frustum from the projection matrix

GLM provides a way to declare a projection matrix:
projectionMatrix = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 1000.f);
From this, I want to be able to check to see if bounding boxes are in my frustum. How do I obtain the frustum planes or whatever it is I would need to calculate this from the projection matrix? Is this even the correct way to do it?
This will help: http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf
Also notice that in order to extract the frustum you will need to extract it using model and view matrix as well otherwise you need to apply model and view transformation on the bounding boxes in order to perform the test.

gluLookAt and glFrustum with a moving object

Original Question/Code
I am fine tuning the rendering for a 3D object and attempting to implement a camera following the object using gluLookAt because the object's center y position constantly increases once it reaches it's maximum height. Below is the section of code where I setup the ModelView and Projection matrices:
float diam = std::max(_framesize, _maxNumRows);
float centerX = _framesize / 2.0f;
float centerY = _maxNumRows / 2.0f + _cameraOffset;
float centerZ = 0.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(centerX - diam,
centerX + diam,
centerY - diam,
centerY + diam,
diam,
40 * diam);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0., 0., 2. * diam, centerX, centerY, centerZ, 0, 1.0, 0.0);
Currently the object displays very far away and appears to move further back into the screen (-z) and down (-y) until it eventually disappears.
What am I doing wrong? How can I get my surface to appear in the center of the screen, taking up the full view, and the camera moving with the object as it is updated?
Updated Code and Current Issue
This is my current code, which is now putting the object dead center and filling up my window.
float diam = std::max(_framesize, _maxNumRows);
float centerX = _framesize / 2.0f;
float centerY = _maxNumRows / 2.0f + _cameraOffset;
float centerZ = 0.0f;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(centerX - diam,
centerX,
centerY - diam,
centerY,
1.0,
1.0 + 4 * diam);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(centerX, _cameraOffset, diam, centerX, centerY, centerZ, 0, 1.0, 0.0);
I still have one problem when the object being viewed starts moving it does not stay perfectly centered. It appears to almost jitter up by a pixel and then down by 2 pixels when it updates. Eventually the object leaves the current view. How can I solve this jitter?
Your problem is with the understanding what the projection does. In your case glFrustum. I think the best way to explain glFrustum is by a picture (I just drew -- by hand). You start of a space called Eye Space. It's the space your vertices are in after they have been transformed by the modelview matrix. This space needs to be transformed to a space called Normalized Device Coordinates space. This happens in a two fold process:
The Eye Space is transformed to Clip Space by the projection (matrix)
The perspective divide {X,Y,Z} = {x,y,z}/w is applied, taking it into Normalized Device Coordinate space.
The visible effect of this is that of kind of a "lens" of OpenGL. In the below picture you can see a green highlighted area (technically it's a 3 volume) in eye space that, is the NDC space backprojected into it. In the upper case the effect of a symmetric frustum, i.e. left = -right, top = -bottom is shown. In the bottom picture an asymmetric frustum, i.e. left ≠ -right, top ≠ -bottom is shown.
Take note, that applying such an asymmetry (by your center offset) will not turn, i.e. rotate your frustum, but skew it. The "camera" however will stay at the origin, still pointing down the -Z axis. Of course the center of image projection will shift, but that's not what you want in your case.
Skewing the frustum like that has applications. Most importantly it's the correct method to implement the different views of left and right eye an a stereoscopic rendering setup.
The answer by Nicol Bolas pretty much tells what you're doing wrong so I'll skip that. You are looking for an solution rather than telling you what is wrong, so let's step right into it.
This is code I use for projection matrix:
glViewport(0, 0, mySize.x, mySize.y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovy, (float)mySize.x/(float)mySize.y, nearPlane, farPlane);
Some words to describe it:
glViewport sets the size and position of display place for openGL inside window. Dunno why, I alsways include this for projection update. If you use it like me, where mySize is 2D vector specifying window dimensions, openGL render region will ocuppy whole window. You should be familiar with 2 next calls and finaly that gluPerspective. First parameter is your "field of view on Y axis". It specifies the angle in degrees how much you will see and I never used anything else than 45. It can be used for zooming though, but I prefer to leave that to camera operating. Second parameter is aspect. It handles that if you render square and your window sizes aren't in 1:1 ratio, it will be still square. Third is near clipping plane, geometry closer than this to camera won't get rendered, same with farPlane but on contrary it sets maximum distance in what geometry gets rendered.
This is code for modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt( camera.GetEye().x,camera.GetEye().y,camera.GetEye().z,
camera.GetLookAt().x,camera.GetLookAt().y,camera.GetLookAt().z,
camera.GetUp().x,camera.GetUp().y,camera.GetUp().z);
And again something you should know: Again, you can use first 2 calls so we skip to gluLookAt. I have camera class that handles all the movement, rotations, things like that. Eye, LookAt and Up are 3D vectors and these 3 are really everything that camera is specified by. Eye is the position of camera, where in space it is. LookAt is the position of object you're looking at or better the point in 3D space at which you're looking because it can be really anywhere not just center object. And if you are worried about what's Up vector, it's really simple. It's vector perpedicular to vector(LookAt-Eye), but becuase there's infinite number of such vectors, you must specify one. If your camera is at (0,0,0) and you are looking at (0,0,-1) and you want to be standing on your legs, up vector will be (0,1,0). If you'd like to stand on your head instead, use (0,-1,0). If you don't get the idea, just write in comment.
As you don't have any camera class, you need to store these 3 vectors separately by yourself. I believe you have something like center of 3D object you're moving. Set that position as LookAt after every update. Also in initialization stage(when you're making the 3D object) choose position of camera and up vector. After every update to object position, update the camera position the same way. If you move your object 1 point up at Y axis, do the same to camera position. The up vectors remains constant if you don't want to rotate camera. And after every such update, call gluLookAt with updated vectors.
For updated post:
I don't really get what's happening without bigger frame of reference (but I don't want to know it anyway). There are few things I get curious about. If center is 3D vector that stores your object position, why are you setting the center of this object to be in right top corner of your window? If it's center, you should have those +diam also in 2nd and 4th parameter of glOrtho, and if things get bad by doing this, you are using wrong names for variables or doing something somewhere before this wrong. You're setting the LookAt position right in your updated post, but I don't find why you are using those parameters for Eye. You should have something more like: centerX, centerY, centerZ-diam as first 3 parameters in gluLookAt. That gives you the camera on the same X and Y position as your object, but you will be looking on it along Z axis from distance diam
The perspective projection matrix generated by glFrustum defines a camera space (the space of vertices that it takes as input) with the camera at the origin. You are trying to create a perspective matrix with a camera that is not at the origin. glFrustum can't do that, so the way you're attempting to do it simply will not work.
There are ways to generate a perspective matrix where the camera is not at the origin. But there's really no point in doing that.
The way a moving camera is generally handled is by simply adding a transform from the world space to the camera space of the perspective projection. This just rotates and translates the world to be relative to the camera. That's the job of gluLookAt. But your parameters to that are wrong too.
The first three values are the world space location of the camera. The next three should be the world-space location that the camera should look at (the position of your object).

How to declare gluPerspective in OpenGL?

How can I actually declare gluPerspective?
I mean I am drawing a spiral and I am setting z = -50 in the beginning and looping until the circle is complete. So what will be the declaration of gluPerspective?
Somewhere at the top of the source file you want to use gluPerspective in:
#include <GL/glu.h>
and then you can use gluPerspective with no problem.
I'm not really sure what you mean.gluPerspective() is typically used once when the program starts, then whenever the window is resized, to define the way primitives are projected to the screen.
You do that as so:
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
gluPerspective(60,screenWidth/screenHeight,0.1,1000);
I strongly recommend that you have a look at the official OpenGL red book chapter 3, Viewing. There you'll find a good introduction to perspective projection.
Edit after comment:
Well if you would read the chapter carefully you would read this:
You can manufacture a viewing transformation in any of several ways, as described next. You can also choose to use the default location and orientation of the viewpoint, which is at the origin, looking down the negative z-axis.
and
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble near, GLdouble far);
Creates a matrix for a symmetric perspective-view frustum and multiplies the current matrix by it. fovy is the angle of the field of view in the x-z plane; its value must be in the range [0.0,180.0]. aspect is the aspect ratio of the frustum, its width divided by its height. near and far values the distances between the viewpoint and the clipping planes, along the negative z-axis. They should always be positive.