Perspective view matrix for Y-down coordinate system - opengl

I have this little toy graphics library in rust I tinker with to learn OpenGL. I initially intended it to do only low-res 2D stuff on it, so I set up the coordinate system for pixel-space orthographic rendering, a system with (0, 0) at the top-left corner of the screen that allows sprites to be "snapped" to pixels for an 8/16-bit style.
Now I'm thinking it would be fun to add a perspective camera to render the sprites in perspective as a sort of paper-doll theater thing, sort of inverse 2.5D, world is 3D but characters are 2D.
So I use the excellent tutorial here:
https://learnopengl.com/Getting-started/Camera
Great stuff, but the algorithm calls for deriving from the up-vector [0, 1, 0], so my world renders upside down (since my "up" is [0, -1, 0]). If I do set the "up" as [0, -1, 0] I instead get the X-axis extending from right to left, as if negating "up" rotates 180° around the Z-axis, which again isn't what I want.
Is it at all possible to do a perspective camera view matrix with a typical orthographic world Y-down coordinate system, and how would that be done practically using e.g. glm?

The trick with the reflection matrix works; The orientation and position of all objects on screen works out and camera movement produces the expected results.
This page is what tipped me off on the fact that the only difference between left and right-hand view matrixes is the direction of the X-axis, and that mirroring/reflections changes the handedness:
https://www.realtimerendering.com/blog/left-handed-vs-right-handed-viewing/
That should make sense, if you compare your hands they only differ in what side the thumb is on, not in what direction the fingers are pointing.
The solution:
Create a standard right-handed view matrix, but define the up-vector as [0, -1, 0]
Create a reflection matrix that flips the view around the Y-axis:
[-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0
0, 0, 0, 1]
multiply the reflection and view matrix:
reflection_matrix * view_matrix;
This works in OpenGL, but allegedly DirectX works with row-major matrices, so you have to take that into account when multiplying.
This is the code I use to produce the view matrix:
let eye: glm::Vec3 = glm::vec3(160.0, 90.0, 300.0);
let target: glm::Vec3 = glm::vec3(160.0, 90.0, 0.0);
let cam_forward = glm::normalize(&(&eye - &target));
let up = glm::Vec3::new(0.0, -1.0, 0.0);
let cam_right = glm::normalize(&glm::cross(&up, &cam_forward));
let cam_up = glm::normalize(&glm::cross(&cam_forward, &cam_right));
let mut mx_view = glm::look_at(&eye, &target, &cam_up);
let mut mirr: glm::Mat4 = glm::Mat4::identity();
mirr[0] = -1.0;
mx_view = mirr * mx_view;
The camera looks down the Z-axis of my 320 by 180 pixel world.
This is of-course in rust, but, based on the excellent nalgebra-glm crate which pretty closely replicates what glm does for C++, so I think anyone using C++ should also be able to follow.

I do not recommend changing the camera matrix.
Instead, try to use glClipControl(GL_UPPRER_LEFT,GL_ZERO_TO_ONE); in this situation, the front face will be CW.
Or simply multiply the projection matrix P[1][1] by -1.

Related

Z value always 1 or -1 when using `glm::perspective`

I'm trying to teach myself the ways for 3D programming with OpenGL, however I am struggling with some things, especially projection matrices.
I defined some vertices for a cube and successfully handed them to my graphics processor. The cube goes from xyz -0.5 to xyz 0.5 respectively, which gets rendered fine.
To move it into my world coordinate system, I am using this model matrix:
auto model = glm::mat4(
glm::vec4(1, 0, 0, 0),
glm::vec4(0, 1, 0, 0),
glm::vec4(0, 0, 1, 0),
glm::vec4(0, 0, 0, 1)
);
model = glm::translate(model, glm::vec3(0.f, 0.f, 495.f));
model = glm::scale(model, glm::vec3(100.f, 100.f, 100.f));
This successfully moves my cube to (-50, -50, 445) -> (50, 50, 545) so its now centered in the 200x200x1000 world coordinates I defined for myself.
My camera / view matrix is
auto view = glm::lookAt(
glm::vec3(0.f, 0.f, 5.f),
glm::vec3(0.f, 0.f, 0.f),
glm::vec3(0.f, 1.f, 0.f)
);
which moves the cube slightly closer, changing the z coordinate to 440 and 540 respectively. I don't understand why this is happening but I guess it has something to do with glm expecting a right hand coordinate system while I am working with a left handed one? While this is not why I am posting this question, I would be happy if someone would clear it up for me.
Now to my actual problem: I am trying to make use of glm::perspective. I call it like this:
auto perspective = glm::perspective(glm::radians(55.f), 1.f, 0.f, 1000.f);
If I'm not mistaken, at a z value of 440 I can expect the clipping area to go from roughly -229 to 229, so I would expect that bottom right cube vertex at (-50,-50) is visible. I calculated this by drawing the frustum in 2D, when I noticed that I should be able to calculate the height of any distance to the camera using tan(alpha / 2) * distToCamera = maxVisibleCoordinate (working with a 1:1 aspect ratio). Is this a correct assumption? Here is my terrible drawing, maybe you can tell that I have a wrong understanding of something with it:
In the final step I am trying to get all this together in my vertex shader using
gl_Position = projection * view * model * vec4(pos.x, pos.y, pos.z, 1.0);
which yields a perfectly reasonable result for the x and y value but the z value is always -1 which is, as far as I know, just right for not being displayed.
For my front-bottom-left vertex of the cube (-0.5, -0.5, -0.5) the result is (-96.04, -96.04, -440, -440), normalized to (-0.218, -0.218, -1).
For my back-top-right vertex of the cube (0.5, 0.5, 0.5) the result is (96.04, 96.04, -550, -550), normalized to (0.218, 0.218, -1).
What am I getting wrong, that my z value is lost and just set to -1 instead? When playing around with the camera position, the best I can get is getting it to 1, which also results in an empty window and is definitely not what I would expect.
A projection matrix is like this:
In the picture, f is for zfar and n is for znear.
As you can see, if you put znear = 0, the term at the 4th column become zero, which is incorrect. Also, -(f+n)/(f-n) = -1, which is incorrect too.
So, the conclusion is, znear cannot be zero. It is usually a small value, for example, 0.1
Since Amadeus already answered the question correctly, I'm going to just use this space to add some clarifying information about why it's correct.
We can refer back to the diagram you provided to explain what the problem is: You have two planes, the near plane and the far plane, representing the range at which you may view objects. What the Perspective Matrix does is it takes everything in between those two planes, within the Frustrum that you've defined (mathematically a cone, but our monitors are rectangular, so...) and maps them onto the flat Near-plane to create the final image. In a sense, you can think of the Near Plane as representing the monitor.
So given this context, if you were to set the Near Plane's distance to 0, meaning it was identical to the camera, what would happen? Well, in a cone it would set the plane to a single point, and in a frustrum, it's the same. You cannot view objects drawn onto a single point. You need a surface with actual surface area to draw onto.
That is why it is inappropriate to set the near value to 0. It would turn the drawing surface into a single point, and you cannot mathematically render any objects on a single point. Hence why the essential mathematical formulas backing the matrix will break down and result in bad outcomes if you try to do so anyways.

glm::lookat, perspective clarification in OpenGL

All yalls,
I set up my camera eye on the positive z axis (0, 0, 10), up pointing towards positive y (0, 1, 0), and center towards positive x (2, 0, 0). If, y is up, and the camera is staring down the negative z axis, then x points left in screen coordinates, in right-handed OpenGL world coordinates.
I also have an object centered at the world origin. As the camera looks more to the left (positive x direction), I would expect my origin-centered object to move right in the resulting screen projection. But I see the opposite is the case.
Am I lacking a fundamental understanding? If so, what? If not, can anyone explain how to properly use glm to generate view and projection matrices, in the default OpenGL right-handed world model, which are sent to shaders?
glm::vec3 _eye(0, 0, 10), _center(2, 0, 0), _up(0, 1, 0);
viewMatrix = glm::lookAt(_eye, _center, _up);
projectionMatrix = glm::perspective(glm::radians(45), 6./8., 0.1, 200.);
Another thing I find interesting is the red line in the image points in the positive x-direction. It literally is the [eye -> (forward + eye)] vector of another camera in the scene, which I extract from the inverse of the viewMatrix. What melts my brain about this is, when I use that camera's VP matrices, it points in the direction opposite to the same forward direction that was extracted from the inverse of the viewMatrix. I'd really appreciate any insight into this discrepancy as well.
Also worth noting: I built glm 0.9.9 via cmake. And I verified it uses the right-hand, [-1, 1] variants of lookat and perspective.
resulting image:
I would expect my origin-centered object to move right in the resulting screen projection. But I see the opposite is the case.
glm::lookAt defines a view matrix. The parameters of glm::lookAt are in world space and the center parameter of glm::lookAt defines the position where you look at.
The view space is the local system which is defined by the point of view onto the scene.
The position of the view, the line of sight and the upwards direction of the view, define a coordinate system relative to the world coordinate system. The view matrix transforms from the wolrd space to the view (eye) space.
If the coordiante system of the view space is a Right-handed system, then the X-axis points to the left, the Y-axis up and the Z-axis out of the view (Note in a right hand system the Z-Axis is the cross product of the X-Axis and the Y-Axis).
The line of sight is the vector form the eye position to the center positon:
eye = (0, 0, 10)
center = (2, 0, 0)
up = (0, 1, 0)
los = center - eye = (2, 0, -10)
In this case, if the center of the object is at (0, 0, 0) and you look at (0, 0, 2), the you look at a position at the right of the object, this means that the object is shifted to the left.
This will change, if you change the point of view e.g. (0, 0, -10) or flip the up vector e.g. (0, -1, 0).

Apply multiple transformations to view

I am working on a OpenGL ES application using C++. I run the code on iOS with a few wrapper Obj-c classes that enable me to use gestures (like pan, pinch, rotation).
I use orthographic projection. The code only draws a simple quad for now, and I want to be able to apply any transformations to it using user gesture. That means move it, zoom in and out, rotate (some other derived gestures, like zoom using double-tap etc.).
I thought it will be fine if I simply store origin, scale and angle floats and construct Matrix using each of these, then multiply. That works for simple translation and scale, however, I can't rotate around any point. Now I can't figure out what do I need to be able to rotate around some point, translate, rotate some more around different point.
Here is example how I initialize my matrix
Mat4 Mat4::translation(float tx, float ty)
{
float a[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1 };
return Mat4(a);
}
Mat4 Mat4::rotation(float angle)
{
float a[16] = { cosf(angle), sinf(angle), 0, 0, -sinf(angle), cosf(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
return Mat4(a);
}
How can I apply any number of transformations to 2D view in OpenGL(ES), using matrices? These transformations are caused by gestures (pan, pinch, rotate). For pinch and rotation, I know amount and center point in screen coordinates.
I believe your problem boils down to finding out with respect to which reference frame you define your transformations.
In practice, you can specify any trasformation (i.e. translation and rotation in 3D) as a 4x4 matrix, like you've done in your example. If they are defined with respect to the current pose of the object, then you can apply a new one by multiplying the new matrix on the right. If they are specified with respect to a fixed reference frame (e.g. your screen origin which does not move) then you pre-multiply them.
Say you apply two rotations, defined by T_rot_1 and T_rot_2
If transformations are defined wrt reference frame attached to the object:
T_final_wrt_fixed = T_rot_1_wrt_obj * T_rot_2_wrt_obj
If transformations are defined wrt reference to fixed frame:
T_final_wrt_fixed = T_rot_2_wrt_fixed * T_rot_1_wrt_fixed
For more info, check out these slides.
Rotation around arbitrary point is done by translation of the point to origin, rotation around origin and then translating back.

directx camera position is confusing me

I have just read the following:
Let's say we want to have the player view an object located at the
world's origin (0, 0, 0). We want the player to be viewing the object
from the coordinates (100, 100, 100). To do this we need to build a
matrix containing this data. We build this matrix using a function
called XMMatrixLookAtLH(). Here is how you use it:
XMVECTOR vecCamPosition = XMVectorSet(100, 100, 100, 0);
XMVECTOR vecCamLookAt = XMVectorSet(0, 0, 0, 0);
XMVECTOR vecCamUp = XMVectorSet(0, 1, 0, 0);
XMMATRIX matView = XMMatrixLookAtLH(vecCamPosition, vecCamLookAt, vecCamUp);
from: http://www.directxtutorial.com/Lesson.aspx?lessonid=111-6-1
I do not understand why if you want to look at an object located at 0, 0, 0, you should position the camera at 100, 100, 100... surely that would mean the camera was way past it... and then looking back at it?
thing is, if I set my camera to 0.0, 0.0, -1.0 and have my objects at a z index of 1.0... my camera can no longer see my objects...
I don't get it?
It sounds like you have 2D objects "facing" away from the camera when positioned at (0, 0, -1). If you reverse your ordering on your triangle vertices, they should show up.
You are fine to put the camera wherever you wish. If you are looking at 3D objects, you should have no issues.
In DirectX, 2D objects can only be seen when their vertices are in clockwise ordering as seen from the camera (though you can turn this off, or change it to discard counter-clockwise triangles if you wish). DirectX uses the clockwise vs counter-clockwise ordering to discard faces so it doesn't draw the backs of 3D objects.

OpenGL simultaneous translate and rotate around local axis

I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons:
Button 1 rotates the viewport around X and Y depending on X/Y mouse drags.
Button 2 translates the viewport around X and Y depending on X/Y mouse drags.
Button 3 "zooms" the viewport by translating along Z.
The code is simple:
glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);
^ the rotation works, but the translation works according to world axis.
My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.
EDIT
I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis.
http://i45.tinypic.com/2lnu3rs.jpg
Ok, the image makes things a bit clearer.
If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.
Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.
Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:
glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);
But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.
glRotate(-CRy, 0, 1, 0);
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);
I think this will give you the kind of camera you're mentioning in your image.
I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:
Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.
To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.
First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.
Quat hRot(rotX, 0, 0, 1); // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis); // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot; // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;
Hope this helps you solve your problem.
glRotate always works around the origin. If you do:
glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();
Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.
However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.