How to set up OpenGL perspective correction? - opengl

I am dealing with an experimental setup where a simple picture is being displayed to a gene-modified fly. The picture is projected on a screen with certain distances to that fly.
Now it's my turn to set up the perspective correction, so that the displayed image, for example a horizontal bar, appears wider in a longer distance to the fly's point of view (experimental setup) . The code now looks like this:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(!USEFRUSTUM)
gluPerspective(90.0f, 2 * width/height, 0.1f, 100.0f);
else
glFrustum(92.3f, 2.3f, -25.0f, 65.0f, 50.0f, 1000.0f);
The values were entered by someone a few years ago and we figured out they are not accurate anymore. However, I am confused which values to enter or to change to make the projection work properly, because as you can see in the experimental setup the fly's field of view is a bit tilted.
I thought about those values:
fovy = angle between a and c
measure width and height on the projection screen
but what is zNear? Should I measure the distance from fly to the top or the bottom of the screen? I dont't get why somebody entered 0.1f, cause that seems for me too near.
How can I know the value of zFar? Is it the maximum distance of an object to the fly?
I got my information on glPerspective from: https://www.ntu.edu.sg/home/ehchua/programming/opengl/CG_BasicsTheory.html
I also checked Simplest way to set up a 3D OpenGL perspective projection , but this post doesn't treat my experimental setup, which is the source of my confusion.
Thank you for any help!

This is one of the prime examples where the frustum method is easier to use than perspective. A frustum is essentially a clipped pyramid. Imagine your fly at the tip of a four sided, tilted pyramid. The near value gives the distance to the pyramid's base and the left, right, bottom and top the perpendicular distance of each side of the pyramids base to the tip. It's perfectly reasonable that the tip is "outside" of the base area. Or in case of your fly the center might be just above the "left" edge.
So assuming your original picture we have this:
"Near" gives the "distance" to the projection screen and right (and of course also top, bottom and left) the respective distances of the tip, err, fly perpendicular to the edges of the screen.
"far" is not important for the projective features and is used solely for determining the depth range.
So what you can program into is the following:
double near = ${distance to screen in mm};
double left = ${perpendicular to left screen edge in mm};
double right = ${perpendicular to right screen edge in mm};
double top = ${perpendicular to top screen edge in mm};
double bottom = ${perpendicular to bottom screen edge in mm};
double s = ${scaling factor from mm into OpenGL units};
double zrange = ${depth range in OpenGL units the far plane is behind near};
glFrustum(s*left, s*right, s*bottom, s*top, s*near, s*near + zrange);
Keep in mind that left, right, top and bottom may be negative. So say you want a symmetrical (unshifted) view in one direction, that left = -bottom and a shift is essentially adding/subtracting to both the same offset.

Related

How to calculate near and far plane for glOrtho in OpenGL

I am using orthographic projection glOrtho for my scene. I implemented a virtual trackball to rotate an object beside that I also implemented a zoom in/out on the view matrix. Say I have a cube of size 100 unit and is located at the position of (0,-40000,0) far from the origin. If the center of rotation is located at the origin once the user rotate the cube and after zoom in or out, it could be position at some where (0,0,2500000) (this position is just an assumption and it is calculated after multiplied by the view matrix). Currently I define a very big range of near(-150000) and far(150000) plane, but some time the object still lie outside either the near or far plane and the object just turn invisible, if I define a larger near and far clipping plane say -1000000 and 1000000, it will produce an ungly z artifacts. So my question is how do I correctly calculate the near and far plane when user rotate the object in real time? Thanks in advance!
Update:
I have implemented a bounding sphere for the cube. I use the inverse of view matrix to calculate the camera position and calculate the distance of the camera position from the center of the bounding sphere (the center of the bounding sphere is transformed by the view matrix). But I couldn't get it to work. can you further explain what is the relationship between the camera position and the near plane?
A simple way is using the "bounding sphere". If you know the data bounding box, the maximum diagonal length is the diameter of the bounding sphere.
Let's say you calculate the distance 'dCC' from the camera position to the center of the sphere. Let 'r' the radius of that sphere. Then:
Near = dCC - r - smallMargin
Far = dCC + r + smallMargin
'smallMargin' is a value used just to avoid clipping points on the surface of the sphere due to numerical precision issues.
The center of the sphere should be the center of rotation. If not, the diameter should grow so as to cover all data.

the order of translate and scale for zoom and pan

first thing I want to do is translating to the center of the screen and draw all of the objects from there.
then I would like to apply tranlsate for panning and scale for zoom. I want to zoom relative to a center point ! so how should be the order of them so that it works ?
glTranslatef(width/2, height/2, 0);
gltranslate(centerX,centerY); // go to center point
glscale(zoom);
glTranslatef(offset.x/zoom, offset.y/zoom, offset.z/zoom); // pan
I tried the above order but it doesn't go to the center point and it always zoom relative to (0,0).
I suppose you are drawing a square with both x and y between 0,1.
first you have to translate to the point the scaled object should be:
glTranslate3f(centerX,centerY,0);
glScale(zoom);
glTranslatef(-0.5f, -0.5f,0); // to the middle
draw stuff
opengl executes the transformations in reverse order since it's a pipeline.
reading the above sequence in the bottom-up direction will give the key.

How to know the plane size in units?

Well the thing is, that I wan't to picture mazes with a different width and height. I'm drawing them in units and my question is, how can I get the plane viewable dimensions in units that I would know how deep inside the screen I would have to draw my maze in order it would be fully seeable. For perspective view I use "::gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 1.0f, 100.0f);"
For example how I get the near plane dimensions(width and height) in OpenGL units or the far plane or any plane between those planes. If I want to picture something entirely seeable I need to know the plane dimensions in OpenGL units or is there another way?
A bit of trigonometry will tell you that: h_near = 2*near*tan(fovy/2) and the same for far: h_far = 2*far*tan(fovy/2)
Then, the ratio will give you the width.
For the "proof", just consider the right-angled triangle formed by the line of view, the vertical of the plane of rendering and back. The length of the line of view is near or far (depending), the angle at the eye position is fovy/2 (i.e. half the view angle) and the vertical on the plane is h_near/2 or h_far/2, as we only get half-way to the plane. Then, the tangent of the angle on a right-angled triangle is equal to the far-side divided by the near-side ...

OpenGL rubiks cube - face rotation with mouse

I am working on my first real OpenGL Project. It is a 3x3x3 Rubiks Cube. Here is a link to a simple screenshot of what i have so far(my rubiks cube)
Rotating the cube is done with dragging the mouse while holding the right mouse button. This works using the example of a arcball from NeHe Tutorials(NeHe Arcball)
I have the class singleCubes which represents one cube via 6 actual quads, stored in a display list that can be used in it´s draw method.
Class ComplexCube has an array of 3x3x3 singleCubes and is used as interface when interacting with the complete rubiks cube.
Now i want to rotate each specific face according to the mousedragging with left mouse button down. I use picking to get the id of the corresponding side of the single cube the user clicked on. This works also. So i click on a side of one cube on a face and depending on the direction of the dragging i set a rotation and offset factor of the cubes that get affected. (i also want to implement that u actually see the face rotate instead of just changing the color)
Now my Problem is that when i rotate the rubiks cube in any direction with right mouse dragging, it becomes upside down for example. So when i click on a side and want to rotate the face to the right, it´s going the wrong direction because i can´t keep track if the cube is upside down or whatever. Due to the use of the arcball rotation i dont have a x- or y-rotation angle which i could use to determine this.
Question 1: How can i keep track or later on get the information if the cube is upside down, tilted etc in order to translate the mouse dragging information(when rotating one face) when using the arcball example linked above?
// In render function
glPushMatrix();
{
glMultMatrixf(Transform.M); // Rotation applied by arcball object
complCube.draw(); // Draw all the cubes using display lists
}
glPopMatrix();
Setup: C++ with Microsoft Visual Studio 2008, GLEW, freeglut
You could use gluUnProject to convert mouse coordinates to 3d space and get a vector (difference between two points). This vector could then be used to apply a "force" to the selected face. Since gluUnProject uses the projection matrix, it would automatically deal with the orientation of the camera.
Basically, once you get your "force" vector, you project it onto the three axes (so onto (1,0,0), (0,1,0), (0,0,1)). Then choose the one with the largest magnitude. Then you have to convert this direction into a rotation axis as in the diagram below (sorry for the bad paint skills):
So what we have is the "force" vector in black and the selected rubiks face in grey. To get the rotation axis, just take the cross product the "force" vector with the normal of the selected face. This gives the red arrow. From that, you should be able to rotate your cubes in the right direction.
Edit to answer the question in more detail
So continuing from my explanation, I will give an example of how this will help you. Let's first assume your screen is 800x800 pixels and your rubiks cube is always centred. Now lets also assume that, as per your drawings in the comments, that we are in the case on the left.
We drag the mouse and get two positions which using gluUnProject are transformed into world coordinates (the numbers were chosen to show my point, not by any calculation):
p1 : (600, 600) -> (1, -0.5, 0)
p2 : (630, 605) -> (1.3, -0.505, 0)
Now we get the difference vector: p2 - p1 = v = (0.3, -0.05, 0). The reason that I was saying to "project onto the three axes" is so that you extract your major movement (which in this case is 0.3 in the x axis) (since the rubiks cube can't rotate along diagonals). To do the "projection" you just have to take the x, y, z axes individually and create vectors from them so you wind up with:
v1 = (0.3, 0, 0)
v2 = (0, -0.05, 0)
v3 = (0, 0, 0)
Now take the magnitudes and discard the smallest vectors, so we are left with the vector v1 = (0.3, 0, 0). This is your movement vector in world space. Now you take the cross product of that vector, with the normal vector of the selected face (which in this case would be (0, 0, 1)). This gives you a vector which points down (0, 1, 0) (after normalization) (in this step you will probably also have to extract the largest component only (0.02, 1.2, 0.8) -> (0, 1, 0) otherwise you would get bizarre rotations if your camera was not pointing directly along the main axes). You can now use that vector as the rotation axis and use 0.3 as your rotation amount (if it rotates in the opposite direciton to that expected, just put a -).
Now how does this help if your cube is upside down? Suppose we click on the screen in the same way. We now get:
p1 : (600, 600) -> (-1, 0.5, 0)
p2 : (630, 605) -> (-1.3, 0.505, 0)
See the difference in the world coordinates? They are inverted! So when you take the difference vector p2 - p1 = v = (-0.3, 0.05, 0). Extracting the largest component vector gives (-0.3, 0, 0). Doing the cross product once again gives you the rotation axis, but now the rotation is in the opposite direction, which is what you want.
Another reason for the cross product with the normal of the face is that if you were to select the faces on the top (in our drawings), then it would either give a rotation axis along the x or z axes (to the left, or into the screen) which is what you want for the top faces.
Like most of us, you will encounter the famous problem called Gimbal Lock.
see: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=208925
This problem is extremely well documented so there is not much point for me to go into details here. I am sure you will find a ton of information about it.

Setting near plane in OpenGL

i have implemented first person camera in OpenGL, but when i get closer to an object it starts to disappear, so i want to set near plane close to zero so i could get closer to the objects. So if anybody can tell me what is the best way to do that.
Thank you.
Other responses focus on using glut. Glut is not recommended for professional, or even modern, development, and the OP says nothing about using glut - just OpenGL. So, I'll chime in:
A little background
zNear, the distance from the origin to the near clip plane, is one of the parameters used to build the projection matrix:
Projection Matrix
Where
SYMBOL MEANING TYPICAL VALUE
------ -------------- -------------
fov vertical field of view 45 – 90 degrees
aspect Aspect ratio around 1.8 (frame buffer Width / Height)
znear near clip plane +1
zfar far clip plane. 10
SYMBOL MEANING FORMULA
---------- ------------------------------- --------------
halfHeight half of frustum height at znear znear∗tan(fov/2)
halfWidth half of frustum width at znear halfHeight×aspect
depth depth of view frustum zfar−znear
(More nicely-formatted version on http://davidlively.com/programming/graphics/opengl-matrices/perspective-projection/)
When the perspective divide takes place - between the vertex and fragment shaders - the vertices are converted to normalized device coordinates (NDC), in "clip space." In this space, anything that fits in a 2x2x1 (x,y,z) box will be rendered. Any fragments that don't fit in a box with corners (-1, -1, 0) - (+1, +1, +1) will be clipped.
Practical Upshot Being
It doesn't matter what your zNear and zFar values are, as long as they offer sufficient resolution & precision, and zFar > zNear > 0.
Your collision detection & response system is responsible for keeping the camera from getting too close to the geometry. "Too close" is a function of your zNear and geometry bounds. Even if you have a zNear of 1E-9, geometry will get clipped when it gets too close to the clip space origin.
So: fix your collision detection and stop worrying about your zNear.
So if anybody can tell me
gluPerspective.
The near plane is set when you set the projection matrix, either with glFrustum or glOrtho. One of the parameters is the near plane. Notice that the distance to the near plane must be > 0.
You don't have many options.
Cast some rays from the camera (for instance in the 4 corners and in the center), take the shortest minus epsilon, clamped to a decent value > 0. 0.1f will do.
Simply forbid the camera to be here in the first place ! For this you can link it to a sphere in your physics engine, check whether it intersects something, and it it does, move it (how and where to move it is your problem since it's for a large part gameplay. Think of Super Mario Galaxy)
Never set a too little nearPlane. You will run in precision issues with your z-buffer. The farPlane can be quite large though. Values like (0.1, 1000) can be all right depending on your application