I'm doing 3D perspective projection in OpenGL (webgl), doing it myself with uniform-matrices.
Everthing is working fine, but I have an aspect ration of 3:2 (600px x 400px) and this distorts all geometry rendered.
In 2D I used to fix this in the model matrix by dividing x and y through 1 / width and 1 / height respectively.
Now I also have z to worry about and I am pretty clueless how / where to transform z to not distort on my 3:2 aspect ratio.
The model matrix does not seem to offer any opportunity to do this and I don't know where / what to do in the projection matrix.
Edit:
Projection Matrix:
#_pMatrix = [
1, 0.0, 0.0, 0.0,
0.0, 1, 0.0, 0.0,
0.0, 0.0, -(f + n) / (f - n), -1,
0.0, 0.0, -2.0 * n * f / (f - n), 0.0
]
Column major order
Edit 2:
Weird distortions on n < 1
You're missing the *left*, *right*, *top*, *bottom* in your projection matrix.
2*n
----- 0 0 0
r-l
2*n
0 ----- 0 0
t-b
r+l t+b -(f+n)
----- ----- ------ -1
r-l t-b f-n
-2*f*n 0
0 0 ------
f-n
If you define (r-l)/(t-b) = 3/2 such that the viewing volume is the appropriate size for your model, then you should be set.
Here are some slides describing the various projection matrices and how they're derived:
http://www.cs.unm.edu/~angel/CS433/LECTURES/CS433_17.pdf
They're by Edward Angel, the author of Interactive Computer Graphics, which is where I got this matrix. Unfortunately the OpenGL Red Book doesn't seem to work through the math at all.
Related
I'm trying to render a series of 2D shapes (Rectangle, Circle) etc in modern opengl, hopefully without the use of any transformation matrices. I would like for me to be able to specify the coordinates for say a rectangle of 2 triangles like so:
float V[] = { 20.0, 20.0, 0.0,
40.0, 20.0, 0.0,
40.0, 40.0, 0.0,
40.0, 40.0, 0.0,
20.0, 40.0, 0.0,
20.0, 20.0, 0.0 }
You can see that the vertex coordinates are specified in viewport? space (I believe thats what its called). Now, when this get rendered by opengl, it doesnt work because clip space goes from -1.0 to 1.0 with the origin in the center.
What would be the correct way for me to handle this? I initially thought adjusting glClipControl to upper left and 0 to 1 would work, but it didnt. With clip control set to upper left and 0 to 1, the origin was still at the center, but it did allow for the Y-Axis to increase as it moved downward (which is a start).
Ideally, I would love to get opengl to have 0.0,0.0 to be the top left and 1.0, 1.0 to be the bottom right, then I just normalise each vertex position, but I have no idea how to get opengl to use that type of coordinate system.
One can easily do these transformation without matrices in the vertex shader:
// From pixels to 0-1
vPos.xy /= vResolution.xy;
// Flip Y so that 0 is top
vPos.y = (1.-vPos.y);
// Map to NDC -1,+1
vPos.xy = vPos.xy*2.-1.;
I'm trying to determine the position of a cube after the execution of the following code:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(3.0, 2.0, 1.0);
glPushMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(1.0, 2.0, 3.0);
glPushMatrix();
glTranslatef(1.0, 1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glTranslatef(2.0, 2.0, 2.0);
glutWireCube(1.0);
I don't know if i should consider the GL_MODELVIEW matrix or the GL_PROJECTION
matrix. After the first glPushMatrix() the glMatrixMode is changed from GL_MODELVIEW to GL_PROJECTION, so should I ignore the glTranslatef(3.0, 2.0, 1.0), so the final position is of the cube is: (3.0, 4.0, 5.0)?
Lets take just the MODELVIEW matrix stack:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(3.0, 2.0, 1.0);
glPushMatrix();
glPopMatrix(); // nullifies the above push
glTranslatef(2.0, 2.0, 2.0);
The stack starts with one entry; you set it to identity, then multiply it with a translation matrix. PushMatrix creates a copy of this matrix in a new entry atop the older one, on the stack and then you undo this by PopMatrix. So you continue to have the first matrix, which is then multiplied by another translation matrix. Effectively your model view transform, which transforms the cube from its model space into the world space, is
| 1 0 0 5 |
| 0 1 0 4 |
| 0 0 1 3 |
| 0 0 0 1 |
A cube which is centered at (5, 4, 3) in the world space.
Then you go on to set the projection matrix stack with the following
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(1.0, 2.0, 3.0);
glPushMatrix();
glTranslatef(1.0, 1.0, 1.0);
Based on the same logic as above, you've an effective projection matrix of
| 1 0 0 2 |
| 0 1 0 3 |
| 0 0 1 4 |
| 0 0 0 1 |
This isn't a valid projection transformation matrix; neither orthogonal nor perspective. You can create a valid projection matrix using the helper functions glFrustum (for a perspective camera) or glOrtho (for orthographic camera). The GLU functions gluPerspective and gluOrtho2D are slightly more intuitive than their gl counterparts. See the OpenGL FAQ on Using Viewing and Camera Transforms for details.
I'm programming a simple application in OpenGL. I have a model that I want to rotate around its center (local coordinate), then translate it along with its local coordinate, too. I've done two scenarios,
rotate then translate, and vice versa. In the first scenario, I got the rotation correct but the model then translate based on the world coordinate not the model (local) coordinate. When I translate first, I got the translation right but the rotation is no longer around the model center.
here's the code,
glPushMatrix();
// ignore this
/*glRotatef(_zRoll, 0.0, 0.0, 1.0);
glRotatef(_yTilt, 1.0, 0.0, 0.0);
glRotatef(_xPan, 0.0, 1.0, 0.0);*/
glScalef(scale, scale, scale);
glTranslatef(0.0, 0.0, -2.0);
glTranslatef(_xTranslate, _yTranslate, _zTranslate); // The required translation
// The required rotation
glRotatef(_yangle, 0.0, 1.0, 0.0);
glRotatef(_zangle, 0.0, 0.0, 1.0);
glRotatef(_xangle, 1.0, 0.0, 0.0);
glTranslatef(coord.x, coord.y, coord.z); // translate to the model center
glCallList(aHelix);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glPopMatrix();
glutSwapBuffers();
I think I understand now what you're looking for. I will attempt to restate what you want, so please correct me if I am mistaken.
Let's take a simple model with the following vertex coordinates (columns are vertices):
[3 2 3 4]
[5 2 3 2]
[0 0 0 0]
[1 1 1 1]
So the first point is at x=3, y=5, z=0. Drawing this model we get:
If we simply rotate the model (in this case by 43°) and then translate it in the Y direction (up), say 4 units, we get this:
The model has now moved 4 units in the Y direction, but in the world coordinate frame. As I understand it, the result above is what you DO NOT want.
If instead we translate the model in the direction it is pointing after rotation, we get this:
This, I believe, is what you DO want. Here's the code I used.
glTranslatef(3.0, 3.0, 0.0);
glTranslatef(-2.728, 2.924, 0.0);
glRotatef(43.0, 0.0, 0.0, 1.0);
glTranslatef(-3.0, -3.0, 0.0);
I first rotate the model by 43 degrees and then translate it according to the updated local Y direction. I got the hardcoded translation vector by taking the vector [0 4 0 0] and rotating it using the same 43° rotation matrix I used for the model. This isn't straightforward to do programmatically (at least in the fixed pipeline) and it gets worse with subsequent rotations.
It turns out, though, that you get the exact same results (which you can verify manually) by performing the translation by [0 4 0 0] first, followed by the rotation:
glTranslatef(3.0, 3.0, 0.0);
glRotatef(43.0, 0.0, 0.0, 1.0);
glTranslatef(0.0, 4.0, 0.0);
glTranslatef(-3.0, -3.0, 0.0);
Here's the result of the second set of code for comparison:
The problem is that if you do any subsequent rotations/translations, your coordinate systems still get all messed up. To get around this, you need to make sure you're using an accumulator for your rotations and translations and applying them all at once to the original model rather than making small incremental changes. So, for each press of the up-arrow key, add some incremental value to your _yTranslate variable, for each press of the "rotate" button, add or subtract some value from _zangle, and so on.
I am having problems setting the camera such that it points at the origin O(0,0,0) with a degree of 45° to all other axes with all coordinates positive (which should have the same value, of course). You can see in the image the closest I've got
However, as you can see, the x value is negative here, so the camera is on the wrong side of the YZ plane.
The complete compilable project can be found at this revision in a gist.
The relevant matrix multiplications done are
osg::Matrixd rotate_x(
1.0, 0.0, 0.0, 0.0,
0.0, q_cos, -q_sin, 0.0,
0.0, q_sin, q_cos, 0.0,
0.0, 0.0, 0.0, 1.0
);
osg::Matrixd rotate_y(
q_cos, 0.0, q_sin, 0.0,
0.0, 1.0, 0.0, 0.0,
-q_sin, 0.0, q_cos, 0.0,
0.0, 0.0, 0.0, 1.0
);
camera_pos = camera_pos * rotate_x;
camera_pos = camera_pos * rotate_y;
in the file Simple.cpp.
I'm trying to figure out how this works (both mathematically and programmatically). I would prefer solutions which rely as little as possible on openscenegraph, and more on the math side, as I'd like to do the maths myself at first, to get a real grasp of how it works. So no quaternions or other advanced stuff yet, which are not taught in a basic linear algebra university course.
Your rotate_y matrix looks flipped to me.
Starting with rotation matrix from glRotate, I simplified the resulting matrix with xyz = (0,1,0), and I got this (in column major order (standard opengl notation)
q_cos 0 q_sin 0
0 1 0 0
-q_sin 0 q_cos 0
0 0 0 1
However, you're supplying the same matrix in row major order:
osg::Matrixd rotate_y(
q_cos, 0.0, q_sin, 0.0,
0.0, 1.0, 0.0, 0.0,
-q_sin, 0.0, q_cos, 0.0,
0.0, 0.0, 0.0, 1.0
);
-q_sin should be element [2] of the matrix, and q_sin should be element [8], but it looks like you've flipped them.
EDIT
OpenGL and OSG typically represent matrices in column major format, when you see a 4x4 matrix, it is laid out like the following
[0] [4] [8] [12]
[1] [5] [9] [13]
[2] [6] [10] [14]
[3] [7] [11] [15]
Where [0] is the first element of the array, 1 is the second, etc.
When you create a matrix with OSG, you're defining 16 sequential memory elements, from [0] to [15]
osg::Matrixd rotate_y([0], [1], [2]......,[15]);
When you break up the command into 4 rows, it looks like this:
osg::Matrixd rotate_y(
[0], [1], [2], [3],
[4], [5], [6], [7],
[8], [9], [10],[11],
[12],[13],[14],[15]
)
Do you see how this is transposed from the original column vector representation? You need to flip it because the 4x4 matrix examples you find are the internet are represented by column vectors, while you're currently uploading them as row vectors. It doesn't mean that what you're reading is 'wrong', it's just in a different representation.
Rotation matrices rotate a point around the origin. Since you set your initial position to (0,0,500) and apply an x and y rotation, you will be rotating the point (0,0,500) around the origin by acos(q_cos) or asin(q_sin) radians.
Greetings all,
As seen in the image,
I have an object named O (set of linestripes).Its object-coordinate system is (x',y',z').
I translate,rotate this object in my OpenGL scene using following code snippet:
glPushMatrix();
glTranslatef(Oz, Oy,Oz);
glRotatef(rotationX , 1.0, 0.0, 0.0);
glRotatef(rotationY, 0.0, 1.0, 0.0);
glRotatef(rotationZ, 0.0, 0.0, 1.0);
contour->render();
glPopMatrix()
;
I have a point called H ,which is translated to (hx,hy,hz) world coordinates using
glPushMatrix();
glTranslatef(hx,hy,hz);
glPopMatrix();
If I am correct, (Oz,Oy,Oz) and (hx,hy,hz) are world coordinates.
Now,what I want todo is calculate the position of H (hx,hy,hz) relative to O's object-coordinate system.(x',y',z');
As I understood,I can do this by calculating inverse transformations of object O and apply them to point H.
Any tips on this? Does OpenGL gives any functions for inverse-matrix calculation ? If I somehow found inverse-matrices what the order of multiplying them ?
Note : I want to implement "hammer" like tool where at point H ,I draw a sphere with radius R.User can use this sphere to chop the object O like a hammer.I have implemented this in 2D ,so I can use the same algorithm if I can calculate the hammer position
relative to (x',y',z')
Thanks in advance.
Inverting the matrix would be the general solution, but as far as I can see this isn't actually a "general" problem. Rather than undoing an arbitrary transformation, you are trying to do the reverse of a known sequence of transformations, each of which can be inverted very simply. If your object-to-world transformation is:
glTranslatef(Ox, Oy, Oz);
glRotatef(rotationX , 1.0, 0.0, 0.0);
glRotatef(rotationY, 0.0, 1.0, 0.0);
glRotatef(rotationZ, 0.0, 0.0, 1.0);
Then the world-to-object inverse is just:
glRotatef(-rotationZ, 0.0, 0.0, 1.0);
glRotatef(-rotationY, 0.0, 1.0, 0.0);
glRotatef(-rotationX , 1.0, 0.0, 0.0);
glTranslatef(-Ox, -Oy, -Oz);
Basically, just back out each applied transformation in the opposite order originally applied.
Yes, basically you're right that you can perform this operation by the translation matrix
M = O^-1 * H
any like you already guessed you need the inverse of O for this. OpenGL is not a math library though, it only deals with rendering stuff. So you'll have to implement the inversion yourself. Google for "Gauss Jordan" to find one possible algorithm. If you can be absolutely sure, that O consists only of rotation and translation, i.e. no shearing or scaling, then you can shortcut by transposing the upper left 3x3 submatrix and negating the uppermost 3 elements of the rightmost column (this exploits the nature of orthogonal matrices, like rotation matrices, that the transpose is also the inverse, the upper left 3x3 is the rotational part, the inverse of a translation is negating the elements of it's vector which is the rightmost upper 3 elements).