OpenGL camera rotation - opengl

In OpenGL I'm trying to create a free flight camera. My problem is the rotation on the Y axis. The camera should always be rotated on the Y world axis and not on the local orientation. I have tried several matrix multiplications, but all without results. With
camMatrix = camMatrix * yrotMatrix
rotates the camera along the local axis. And with
camMatrix = yrotMatrix * camMatrix
rotates the camera along the world axis, but always around the origin. However, the rotation center should be the camera. Somebody an idea?

One of the more tricky aspects of 3D programming is getting complex transformations right.
In OpenGL, every point is transformed with the model/view matrix and then with the projection matrix.
the model view matrix takes each point and translates it to where it should be from the point of view of the camera. The projection matrix converts the point's coordinates so that the X and Y coordinates can be mapped to the window easily.
To get the mode/view matrix right, you have to start with an identity matrix (one that doesn't change the vertices), then apply the transforms for the camera's position and orientation, then for the object's position and orientation in reverse order.
Another thing you need to keep in mind is, rotations are always about an axis that is centered on the origin (0,0,0). So when you apply a rotate transform for the camera, whether you are turning it (as you would turn your head) or orbiting it around the origin (as the Earth orbits the Sun) depends on whether you have previously applied a translation transform.
So if you want to both rotate and orbit the camera, you need to:
Apply the rotation(s) to orient the camera
Apply translation(s) to position it
Apply rotation(s) to orbit the camera round the origin
(optionally) apply translation(s) to move the camera in its set orientation to move it to orbit around a point other than (0,0,0).
Things can get more complex if you, say, want to point the camera at a point that is not (0,0,0) and also orbit that point at a set distance, while also being able to pitch or yaw the camera. See here for an example in WebGL. Look for GLViewerBase.prototype.display.
The Red Book covers transforms in much more detail.
Also note gluLookAt, which you can use to point the camera at something, without having to use rotations.

Rather than doing this using matrices, you might find it easier to create a camera class which stores a position and orthonormal n, u and v axes, and rotate them appropriately, e.g. see:
https://github.com/sgolodetz/hesperus2/blob/master/Shipwreck/MapEditor/GUI/Camera.java
and
https://github.com/sgolodetz/hesperus2/blob/master/Shipwreck/MapEditor/Math/MathUtil.java
Then you write things like:
if(m_keysDown[TURN_LEFT])
{
m_camera.rotate(new Vector3d(0,0,1), deltaAngle);
}
When it comes time to set the view for the camera, you do:
gl.glLoadIdentity();
glu.gluLookAt(m_position.x, m_position.y, m_position.z,
m_position.x + m_nVector.x, m_position.y + m_nVector.y, m_position.z + m_nVector.z,
m_vVector.x, m_vVector.y, m_vVector.z);
If you're wondering how to rotate about an arbitrary axis like (0,0,1), see MathUtil.rotate_about_axis in the above code.

If you don't want to transform based on the camera from the previous frame, my suggestion might be just to throw out the matrix compounding and recalc it every frame. I don't think there's a way to do what you want with a single matrix, as that stores the translation and rotation together.
I guess if you just want a pitch/yaw camera only, just store those values as two floats, and then rebuild the matrix based on that. Maybe something like pseudocode:
onFrameUpdate() {
newPos = camMatrix * (0,0,speed) //move forward along the camera axis
pitch += mouse_move_x;
yaw += mouse_move_y;
camMatrix = identity.translate(newPos)
camMatrix = rotate(camMatrix, (0,1,0), yaw)
camMatrix = rotate(camMatrix, (1,0,0), pitch)
}

rotates the camera along the world axis, but always around the origin. However, the rotation center should be the camera. Somebody an idea?
I assume matrix stored in memory this way (number represent element index if matrix were a linear 1d array):
0 1 2 3 //row 0
4 5 6 7 //row 1
8 9 10 11 //row 2
12 13 14 15 //row 3
Solution:
Store last row of camera matrix in temporary variable.
Set last row of camera matrix to (0, 0, 0, 1)
Use camMatrix = yrotMatrix * camMatrix
Restore last row of camera matrix from temporary variable.

Related

how do I maintain relative transformation b/w 2 objects after changing transformation of any one without a scenegraph?

Say I have 2 objects, a camera and a cube, both on XZ plane, the cube has some arbitrary rotation, and camera is facing the cube.
now if a transformation R is applied to the camera such that it has a new rotation and position.
I want to move the cube in front of the camera using transformation R1, such that in the view it looks exactly as before R was applied, meaning relative distance, rotation and scale b/w the 2 objects remain same after both R and R1.
Following image gives a gist of the problem.
Assume that there's no scenegraph that we can use.
I've posed the problem mainly in 2D but I'm trying to solve it in 3D, so rotations can have all yaw, pitch and roll, and translations can be anywhere in 3D space.
EDIT:
I forgot to add what I have done so far.
I figured out how to maintain relative distance b/w camera and cube, I can project cube's position to get world to screen point, then unproject the screen point in new camera position to get new world position.
However for rotation, I have tried this
I thought I can apply same rotation as R in R1, this didn't work, it appears to work if rotation happens only in one axis, if rotation happens in more than one axes, it does not work.
I thought I can take delta rotation b/w camera and cube, and simply apply camera's rotation to the cube and then multiply delta rotation, this also didn't work
Let M and V be the model and view matrices before you move the camera, M2 and V2 be the matrices after you move the camera. To be clear: model matrix transforms the coordinates from object local coordinates into world coordinates; a view matrix transforms from world coordinates into clip-space camera coordinates. Consequently V*M*p transforms the position p into clip-space.
For the position on the screen to stay constant, we need V*M*p = V2*M2*p to be true for all p (that's assuming that the FOV doesn't change). Therefore V*M = V2*M2, or
M2 = inverse(V2)*V*M
If you apply the camera transformation on the right (V2 = V*R) then the above expression for M2 can be simplified:
M2 = inverse(R)*M
(that is you apply inverse(R) on the left of the model matrix to compensate).
Alternatively, ask yourself if you really need to keep the object coordinates in the world reference frame. It may be easier to not to apply the view matrix when rendering that object at all; that would effectively keep it relative to the camera at all times without any additional tweaks. That would have better numerical stability too.

Assistance translating a rotated camera

I have implemented camera rotation around a centre entity and now want to add camera translation. I cannot just do centre.xy += mouse.delta.xy as if the camera is rotated facing the z axis and I drag to the right, this will obviously move the camera towards me (because x axis being incremented). In this instance, the centre.z attribute would need to be increased. I suppose I need to incorporate the camera's pitch, yaw and roll properties into this calculation but not sure how to go about it... any suggestions/links?
I also tried playing around with ray casting (which I have implemented), in place of the mouse delta, but to no avail.
EDIT - simple method:
val right = Vector3f(viewMatrix.m00(), viewMatrix.m01(), viewMatrix.m02()).mul(lmb.delta.x)
val up = Vector3f(viewMatrix.m10(), viewMatrix.m11(), viewMatrix.m12()).mul(lmb.delta.y)
val delta = right.add(up)
center.add(delta)
You did not write a lot about how you represent your camera, but I assume the following:
The camera is represented by a focus point centre and three Euler angles that describe the rotation about that focus point. Probably also a distance to the focus point.
I'll explain two ways - one rather simple and one more sophisticated.
Simple Way
Let's recap what you were trying to do:
centre.xy += mouse.delta.xy
This fails when the camera is not aligned with the coordinate system. A more general formulation of this approach would be:
centre += mouse.delta.x * right + mouse.delta.y * up
Here, right is a world-space vector pointing to the right side of the screen and up is a world-space vector pointing upwards. Depending on your mouse delta, you may instead want a down vector.
So, where do we get those vectors from? Easy. The view matrix has all we need. The first row (the first three entries of the row) are the right vector. The second row is the up vector. So, simply get the view matrix, read those vectors, and update the focus center. You might also want to add some scale.
More Sophisticated
In many applications, the panning functionality is designed in a way such that a certain 3D point under the mouse stays under the mouse during panning. This can be achieved in the following way:
First, we need the depth of the 3D point that we want to keep under the mouse. Two common options are the depth of the focus point or the actual depth of the 3D scene under the mouse (which you get from the depth map). I will explain the former.
We first need this depth in Normalized Device Coordinates. To do this, we first calculate the view-projection matrix:
VP = ProjectionMatrix * ViewMatrix
Then, we transform the focus point into clip space:
focusClip = VP * (focus, 1)
(focus, 1) is a 4D vector with a 1 as its last component. Finally, we derive NDC depth as
focusDepthNDC = focusClip.z / focusClip.w
Ok, now we have the depth. So we can calculate the 3D point that we want to keep under the mouse. First, lets invert the view-projection matrix because this allows us to go from clip space to world space:
VPInv = inverse(VP)
Then, the point under the mouse is (I'll call it x):
x = VPInv * (mouseStartNDC.x, mouseStartNDC.y, focusDepthNDC, 1)
mouseStartNDC is the mouse position before the shift. Keep in mind that this needs to be in normalized device coordinates. If you only have screen space coordinates, then:
ndcX = 2 * screenX / windowWidth - 1
ndcY = -2 * screenY / windowHeight + 1
x is again a 4D vector. Do the perspective divide:
x *= 1.0 / x.w
Now we have our 3D point. We just need to find a shift of the camera that keeps this position under the mouse at the mouse location after the shift:
newX = VPInv * (mouseEndNDC.x, mouseEndNDC.y, focusDepthNDC, 1)
Do the perspective divide again:
newX *= 1.0 / newX.w
And finally update your camera center:
centre += (x - newX).xyz
This approach works with any camera model that you can express in matrix form.

cursor orientation openGL c++

I want my 2D sprite to rotate while always facing my cursor.
I am using glad, SDL2 & glm for the math.
the "original" way I tried was to calculate the angle between my Front and my desired LookAt vector of the object and put that angle in degrees into an glm::rotate matrix.
That did not work out for some strange reason.
The other way was to do it within a quat and apply the quat to my model matrix, which did not help either.
My object is rendered with its center at the origin (0,0,0) - so no translation needs to be done for the rotation.
I draw 2 triangles to make a rectangle on which I load my texture.
My model matrix looks like that in theory:
model = rotate * scale;
I then plug it into the shader (where Position is my vec3 Vertex)
position = projection * view * model * vec4(Position, 1.0f);
The first strange thing is, if I hardcode 90.0f as an angle into glm::rotate, my object is actually rotated about 120° clockwise.
If I plug in 80° it actually rotates about ~250° clockwise.
If I plug in 45° it's a perfect 45° clockwise rotation.
All rotations are around the z-axis, eg.:
model = glm::rotate(model, 90.0f, glm::vec3(0.0f,0.0f,1.0f);
If I use a quaternion to simulate an orientation, it gives me angles between 0,2&0,9 radians and my object seems only to rotate between 0.0° & 45° clockwise, no matter where I put my cursor.
If I calculate the angle btw. my two vectors (ObjectLookAt & MousePosition) and store them I get also quite correct angles, but the glm::rotate function does not work as I'd expected.
Finally if I simply code the angle as:
float angle = static_cast<float>(SDL_GetTicks()/1000);
Which starts by one it actually rotates even more weird.
I'd expect it to start to rate by 1° (as it starts with 1 second) and then rotate a full circle around the z axis until there are 360 seconds over.
However it rotates full 360° in about 6 second. so ever "1" that is added on the angle and plugged into glm::rotate as a degree represents 60°?
Is this now a flaw in my logic? Do I not rotate a sprite around the z-axis if it is drawn on a x-y plane?
I also tried the x & y axis just to be safe here, but that didn't work.
I am really stuck here, I (think I) get how it should work in theory, especially as it all happens in "2D", but I cannot get it work..
The first strange thing is, if I hardcode 90.0f as an angle into glm::rotate, my object is actually rotated about 120° clockwise. If I plug in 80° it actually rotates about ~250° clockwise. If I plug in 45° it's a perfect 45° clockwise rotation.
This is, because the function glm::rotate expects the angle in radians (since GLM version 0.9.6).
Adapt your code like this:
model = glm::rotate(model, glm::radians(angel_degrees), glm::vec3(0.0f,0.0f,1.0f);
see also:
glm rotate usage in Opengl
GLM::Rotate seems to cause wrong rotation?
GLM: function taking degrees as a parameter is deprecated (WHEN USING RADIANS)

Relate textures areas of a cube with the current Oculus viewport

I'm creating a 360° image player using Oculus rift SDK.
The scene is composed by a cube and the camera is posed in the center of it with just the possibility to rotate around yaw, pitch and roll.
I've drawn the object using openGL considering a 2D texture for each cube's face to create the 360° effect.
I would like to find the portion in the original texture that is actual shown on the Oculus viewport in a certain instant.
Up to now, my approach was try to find the an approximate pixel position of some significant point of the viewport (i.e. the central point and the corners) using the Euler Angles in order to identify some areas in the original textures.
Considering all the problems of using Euler Angles, do not seems the smartest way to do it.
Is there any better approach to accomplish it?
Edit
I did a small example that can be runned in the render loop:
//Keep the Orientation from Oculus (Point 1)
OVR::Matrix4f rotation = Matrix4f(hmdState.HeadPose.ThePose);
//Find the vector respect to a certain point in the viewport, in this case the center (Point 2)
FovPort fov_viewport = FovPort::CreateFromRadians(hmdDesc.CameraFrustumHFovInRadians, hmdDesc.CameraFrustumVFovInRadians);
Vector2f temp2f = fov_viewport.TanAngleToRendertargetNDC(Vector2f(0.0,0.0));// this values are the tangent in the center
Vector3f vector_view = Vector3f(temp2f.x, temp2f.y, -1.0);// just add the third component , where is oriented
vector_view.Normalize();
//Apply the rotation (Point 3)
Vector3f final_vect = rotation.Transform(vector_view);//seems the right operation.
//An example to check if we are looking at the front face (Partial point 4)
if (abs(final_vect.z) > abs(final_vect.x) && abs(final_vect.z) > abs(final_vect.y) && final_vect.z <0){
system("pause");
}
Is it right to consider the entire viewport or should be done for each single eye?
How can be indicated a different point of the viewport respect to the center? I don't really understood which values should be the input of TanAngleToRendertargetNDC().
You can get a full rotation matrix by passing the camera pose quaternion to the OVR::Matrix4 constructor.
You can take any 2D position in the eye viewport and convert it to its camera space 3D coordinate by using the fovPort tan angles. Normalize it and you get the direction vector in camera space for this pixel.
If you apply the rotation matrix gotten earlier to this direction vector you get the actual direction of that ray.
Now you have to convert from this direction to your texture UV. The component with the highest absolute value in the direction vector will give you the face of the cube it's looking at. The remaining components can be used to find the actual 2D location on the texture. This depends on how your cube faces are oriented, if they are x-flipped, etc.
If you are at the rendering part of the viewer, you will want to do this in a shader. If this is to find where the user is looking at in the original image or the extent of its field of view, then only a handful of rays would suffice as you wrote.
edit
Here is a bit of code to go from tan angles to camera space coordinates.
float u = (x / eyeWidth) * (leftTan + rightTan) - leftTan;
float v = (y / eyeHeight) * (upTan + downTan) - upTan;
float w = 1.0f;
x and y are pixel coordinates, eyeWidth and eyeHeight are eye buffer size, and *Tan variables are the fovPort values. I first express the pixel coordinate in [0..1] range, then scale that by the total tan angle for the direction, and then recenter.

OpenGL chained translations/rotations

I have a generic OpenGL 3D world, centered on (0,0,0) at start. I implemented a standard trackball, based on this code. This implements rotations as small increments/transformations to the current modelview matrix,
// We need to apply the rotation as the last transformation.
// 1. Get the current matrix and save it.
// 2. Set the matrix to the identity matrix (clear it).
// 3. Apply the trackball rotation.
// 4. Pre-multiply it by the saved matrix.
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)objectXform);
glLoadIdentity();
glRotatef(rot_angle, rotAxis.x, rotAxis.y, rotAxis.z);
glMultMatrixf((GLfloat *)objectXform);
This part works perfectly. But then I wantes to implement translations, and I am doing this also as small increments to the modelview matrix,
glTranslatef(-dx, -dy, 0.f);
This also works as expected (no matter how the world is rotated, the translation goes along with the mouse, i.e., the model goes behind the mouse.
The problem comes when I try to rotate after the translation: I want the rotation to be around the world center, but that will not happen after user translations. I tried to store the absolute translation and compensate for it, but obviously it does not work. I did it as folows:
// Translation part, store absolute translation
m_mouseInfo.m_fTotalTranslationX -= dx;
m_mouseInfo.m_fTotalTranslationY -= dy;
glTranslatef(-dx, -dy, 0.f);
...
// Rotation, try to apply the rotation around (0,0,0)
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)objectXform);
glLoadIdentity();
// Try to compensate for the translation and do the rotation aroun (0,0,0) but won't work
glTranslatef(m_mouseInfo.m_fTotalTranslationX, m_mouseInfo.m_fTotalTranslationY, 0.f);
glRotatef(rot_angle, rotAxis.x, rotAxis.y, rotAxis.z);
glTranslatef(-m_mouseInfo.m_fTotalTranslationX, -m_mouseInfo.m_fTotalTranslationY, 0.f);
glMultMatrixf((GLfloat *)objectXform);
How can I store the absolute translation to compensate for it when I apply the rotation and therefore rotate the scene around the origin?
Or, in other words, how can I just rotate the world around the origin when I have cumulative transfromations?
To translate around a point (x,y), first translate by (x,y), then rotate, then translate by -(x,y).
Now, if your world has been transformed by M (some matrix), then the origin of the world before that transformation is located at M^-1 (0,0).
Suppose your world transformation from the original is M, and you want to perform some rotation R, but that rotation should be around the original origin, but the rotation matrix R is expressed in terms rotation around the point (0,0) (as is the style).
Then R' = M R M^-1 will generate a new matrix R' that consists of rotating by R around the original (0,0). Then M' = R' M is the matrix that represents starting with nothing, and then doing M, then doing R around the origin.
If you are doing cumulative transformations on some model, simply keep track of the product of said transformations, along side modifying the scene.
Alternatively, store the original scene, and instead of doing cumulative transformations on it, always apply M to get the current scene.