OpenGL camera is laggy - c++

I was setting up a camera following this tutorial. My problem is that when I move it isn't fluid, it kinda jumps. I'm calculating the MVP whenever the mouse moves using this code:
void motion(int x, int y) {
static bool wrap = false;
if(!wrap) {
int ww = glutGet(GLUT_WINDOW_WIDTH);
int wh = glutGet(GLUT_WINDOW_HEIGHT);
int dx = x - ww / 2;
int dy = y - wh / 2;
const float mousespeed = 0.001;
angles.x += dx * mousespeed;
angles.y += dy * mousespeed;
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;
lookat.x = sinf(angles.x) * cosf(angles.y);
lookat.y = sinf(angles.y);
lookat.z = cosf(angles.x) * cosf(angles.y);
view = glm::lookAt(position, position + lookat, glm::vec3(0, 1, 0));
// move mouse pointer back to the center of the window
wrap = true;
glutWarpPointer(ww / 2, wh / 2);
} else {
wrap = false;
}
}
And then I'm updating the attribute on my 'OnIdele()' function:
void onIdle() {
glUseProgram(program);
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * view * Model;
glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, glm::value_ptr(MVP));
glutPostRedisplay();
}
My question is, is this the right way to implement this? Is there any way to avoid the laggyness?
Also if you don't mind me asking, how exactly does this code work? I know it limits where you can look but I can't seem to make sense of it:
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;

See if increasing mousespeed makes a difference. After you find the distance the mouse has moved, stored in dx and dy, you scale the distance by mousespeed before adding it to the camera's angles. The lower the value of mousespeed the less your mouse movement will affect the angles of your camera, and vice versa.
Also the code you asked about is limiting your camera angles to between 0 and PI * 2, or 0 and 360 degrees.

Related

How to rotate a point around an arbitrary axis?

I want to rotate a point in OpenGL around an arbitrary axis. I want to utilize that to rotate a sphere.
This is what I got so far:
float degreeBetweenTwoVec(glm::vec3 &a, glm::vec3 b)
{
float prod = b.x * a.x + b.y * a.y + b.z * a.z;
float mag_axis = sqrt((b.x * b.x) + (b.y * b.y) + (b.z * b.z));
float mag_vec = sqrt((a.x * a.x) + (a.y * a.y) + (a.z * a.z));
float degree = prod / (mag_axis * mag_vec);
return acos(degree) * 180.0 / PI;;
}
void rotAroundZ(glm::vec3 &point, float degree)
{
glm::vec3 n_point;
n_point.x = (point.x * cos(degree * PI / 180.0)) - (point.y * sin(degree * PI / 180.0));
n_point.y = (point.x * sin(degree * PI / 180.0)) + (point.y * cos(degree * PI / 180.0));
n_point.z = point.z;
point.x = n_point.x;
point.y = n_point.y;
point.z = n_point.z;
}
void rotAroundY(glm::vec3& point, float degree)
{
glm::vec3 n_point;
n_point.x = (point.x * cos(degree * PI / 180.0)) + (point.z * sin(degree * PI / 180.0));
n_point.y = point.y;
n_point.z = ((point.x * -1.0f) * sin(degree * PI / 180.0)) + (point.z * cos(degree * PI / 180.0));;
point.x = n_point.x;
point.y = n_point.y;
point.z = n_point.z;
}
void rotAroundA(glm::vec3& point, glm::vec3 &axis, float zdegree)
{
float xdegree = degreeBetweenTwoVec(axis, glm::vec3{ 1.0f, 0.0f, 0.0f });
float ydegree = degreeBetweenTwoVec(axis, glm::vec3{ 0.0f, 1.0f, 0.0f });
rotAroundZ(point, xdegree);
rotAroundY(point, ydegree);
rotAroundZ(point, zdegree);
rotAroundY(point, -ydegree);
rotAroundZ(point, -xdegree);
}
void rotAObject(Object& obj, glm::vec3 &axis, float degree)
{
axis = glm::normalize(axis);
translate(axis, glm::vec3{ axis.x, axis.y, axis.z });
for (int i = 0; i < obj.vertices.size(); i++)
{
rotAroundA(obj.vertices[i], axis, degree);
}
rotAroundA(obj.mp, axis, degree);
translate(axis, glm::vec3{ axis.x * -1.0f, axis.y * -1.0f, axis.z * -1.0f });
}
This works just fine if the given axis happens to be on one of the global axis. However, if it isn't and the given axis is basiclly rotating around something else. There is some kind of axis it is rotating around but as soon as change the given axis, for example rotating it around the z axis it rotates around a completlly different axis than before. It looks like for every position the given axis can take there is some other axis the object is actually rotating around.
Any help is appreciated!
I recommend to use a rotation matrix. Use glm::rotate(), to set a rotation matrix by axis and angle.
Convert the point to glm::vec4 and transform it by the rotation matrix:
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
glm::mat4 rot_mat = glm::rotate(glm::mat4(1.0f), glm::radians(degree), axis);
glm::vec3 n_point = glm::vec3(glm::vec4(point, 1.0f) * rot_mat);

OpenGL Resize Window -> objects are “moved / translated”

When the resize event of the window is called, the objects are moved out of the viewport / screen.
The link below is a video to show what happening is:
https://drive.google.com/file/d/1dBnOqBDUBNCQrwr7ChFlpS8vbBQ6wfKh/view?usp=sharing
I just found out that it just happens whin using QT Windowing. It did not happend with GLFW... wooow
I use the following code:
void Renderer::resize(int width, int height) {
RendererSettings* settings = RendererSettings::getInstance();
settings->setSize(width, height);
glViewport(0, 0, width, height);
if (camera != nullptr)
{
float aspectRatio = float(width) / float(height);
camera->updateProjectionPerspectiveAspect(aspectRatio);
}
}
I do not change the camera anymore.
The updateProjectionPerspectiveAspect is the same of glFrustum(FoV, aspect, near, far). but the data others parameters are kept the same.
void Camera::setProjectionPerspective(float fieldOfView, float aspectRatio, float near, float far) {
this->fieldOfView = fieldOfView;
this->aspectRatio = aspectRatio;
this->nearFrustum = near;
this->farFrustum = far;
float xmin, xmax, ymin, ymax; // Dimensions of near clipping plane
float xFmin, xFmax, yFmin, yFmax; // Dimensions of far clipping plane
// Do the Math for the near clipping plane
ymax = near * tanf(float(fieldOfView * PI_DIV_360));
ymin = -ymax;
xmin = ymin * aspectRatio;
xmax = -xmin;
// Construct the projection matrix
projectionMatrix = Mat4f::identity();
projectionMatrix[0] = (2.0f * near) / (xmax - xmin);
projectionMatrix[5] = (2.0f * near) / (ymax - ymin);
projectionMatrix[8] = (xmax + xmin) / (xmax - xmin);
projectionMatrix[9] = (ymax + ymin) / (ymax - ymin);
projectionMatrix[10] = -((far + near) / (far - near));
projectionMatrix[11] = -1.0f;
projectionMatrix[14] = -((2.0f * far * near) / (far - near));
projectionMatrix[15] = 0.0f; }
Camera parameter is not null and this event "resize" is called some times during the resizing. The parameters width and height are corrects.
I think your projection Matrix is wrong, mainly because you don't use the variable aspectRatio at all, but the way you do it it looks correct..? (So it's just me guessing :P)
Here is how i did my projection Matrix in C using an aspect ratio argument, maybe this helps
mat4 set_perspective_matrix(GLfloat fov, GLfloat aspect, GLfloat nearPlane, GLfloat farPlane)
{
mat4 p;
GLfloat f = 1.0/ tan(fov * 3.1415926/360.0);
GLfloat c1 = -(farPlane + nearPlane) / (farPlane - nearPlane);
GLfloat c2 = -(2.0 * farPlane * nearPlane) / (farPlane - nearPlane);
p._[0] = f/aspect;
p._[1] = 0.0;
p._[2] = 0.0;
p._[3] = 0.0;
p._[4] = 0.0;
p._[5] = f;
p._[6] = 0.0;
p._[7] = 0.0;
p._[8] = 0.0;
p._[9] = 0.0;
p._[10] = c1;
p._[11] = c2;
p._[12] = 0.0;
p._[13] = 0.0;
p._[14] =-1.0;
p._[15] = 0.0;
return p;
}
Here is a good article describing the setup of a projection matrix: The Perspective Matrix
The problem was on QT Windowing. It was solved using the following code to resize:
void QtOpenGLRenderer::resizeEvent(QResizeEvent* event) {
QSize size = event->size();
if (event->oldSize().isEmpty())
{
initialScreenSize = size;
return;
}
size = parentWidget->size();
float deltaX = size.width() - initialScreenSize.width();
float deltaY = size.height() - initialScreenSize.height();
renderer->resize(size.width() - deltaX, size.height() - deltaY); }

Rotation of object around World Axis OpenGL

I'm trying to make a controllable ball in OpenGL. I'm using my own matrix class to transform the object matrix, but I can't seem to get the Rotation right. I always end up with the ball rotating around the local axis.
This is how it looks right now https://gfycat.com/LongKindBassethound . The long line are the local axis.
So when the ball moves forward the next side movement will be wrong. Theres a function in the matrix class that allows rotation around any axis:
Matrix& Matrix::rotationAxis(const Vector& Axis, float Angle) {
const float Si = sin(Angle);
const float Co = cos(Angle);
const float OMCo = 1 - Co;
Vector Ax = Axis;
Ax.normalize();
m00= (Ax.X * Ax.X) * OMCo + Co;
m01= (Ax.X * Ax.Y) * OMCo - (Ax.Z * Si);
m02= (Ax.X * Ax.Z) * OMCo + (Ax.Y * Si);
m03= 0;
m10= (Ax.Y * Ax.X) * OMCo + (Ax.Z * Si);
m11= (Ax.Y * Ax.Y) * OMCo + Co;
m12= (Ax.Y * Ax.Z) * OMCo - (Ax.X * Si);
m13= 0;
m20= (Ax.Z * Ax.X) * OMCo - (Ax.Y * Si);
m21= (Ax.Z * Ax.Y) * OMCo + (Ax.X * Si);
m22= (Ax.Z * Ax.Z) * OMCo + Co;
m23= 0;
m30= 0;
m31= 0;
m32= 0;
m33= 1;
return *this;
}
I think with this I can take the world direction vectors and transform them to the local space of the object and then rotate around the result. I don't really know how to do that though (matrix of the ball * world vector? That doesn't work). I would really like to avoid quaternions, but if I can't do that I would appreciate suggestions in that direction too.
EDIT: More Info
The transforamtion Code. As you can see I tried different methods that all do the same... So no surprise there that it doesnt work.
Matrix transM, rotX, rotZ;
rotationX = straight;
rotationZ = side;
if (velocity != Vector(0, 0, 0)) {
velocity.X = -0.0005 * DeltaTime;
velocity.X = clamp(velocity.X, 0, FLT_MAX);
velocity.Z = -0.0005 * DeltaTime;
velocity.Z = clamp(velocity.Z, 0, FLT_MAX);
}
velocity.X += speed * -side * DeltaTime;
velocity.Z += speed * straight * DeltaTime;
transM.translation(velocity.X, 0, velocity.Z);
if (velocity.Z != 0 || velocity.X != 0) {
//http://mathworld.wolfram.com/EulerAngles.html
//http://gamedev.stackexchange.com/questions/67199/how-to-rotate-an-object-around-world-aligned-axes
Vector localAxisX = m_Ball * Vector(1, 0, 0);
Vector localAxisZ = m_Ball * Vector(0, 0, 1);
rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime);
//rotX.rotationX(0.5* M_PI * straight * DeltaTime * 3);
//rotZ.rotationZ(0.5* M_PI * side * DeltaTime * 3);
//Matrix fullRotation.rotationYawPitchRoll(Vector(0, 0.5* M_PI * straight * DeltaTime, 0.5* M_PI * side * DeltaTime));
m_Ball = transM * m_Ball * (rotX*rotZ);
}
else {
m_Ball = transM * m_Ball;
}
Draw code with my previous attempt trying to use glRotatef (obviously commented out right now)
void Ball::draw(float DeltaTime) {
glPushMatrix();
glMultMatrixf(m_Ball);
if(rotationX)
glRotatef(0.5* M_PI * rotationX * DeltaTime, 1.0, 0.0, 0.0);
if(rotationZ)
glRotatef(0.5* M_PI * rotationZ * DeltaTime, 0.0, 0.0, 1.0);
g_Model_ball.drawTriangles();
glPopMatrix();
drawAxis();
}
I highly suggest using quaternions to easily handle compound rotations and avoid gimbal lock.
https://en.wikipedia.org/wiki/Gimbal_lock
Ok with regards to your comments and video, You want to rotate around the ball's center. It seems you accumulate your rotations in m_Ball but do a weird transM multiplication. Also you are probably accumulating translations in transM.
Try not to mix your translations and rotations and avoid accumulating them in your m_Ball. You can do something like this.
//transformation matrix
transM.translation(velocity.X, 0, velocity.Z);
//accumulate translations in m_BallT
m_BallT = transM * m_BallT;
//final Rotation
Matrix rotation = rotX * rotZ;
//accumulate rotations in m_BallR
m_BallR = rotation * m_BallR;
//now compute your final transformation matrix from accumulated rotations and translation
m_Ball = m_BallT * m_BallR;
note how m_BallR is just rotations accumulated. Post multiplication will ensure new rotation is applied after accumulated rotations in m_BallR. Finally translate to the final position accumulated in m_BallT. Your ball will rotate about its center and move according to m_BallT.
You could also simply replace the transformation component on your m_BallR to avoid extra matrix multiplications.
Vector newPos(m_Ball.translation().X + velocity.X, terrainNoise.GetHeight(m_Ball.translation().X, m_Ball.translation().Z) + 0.5, m_Ball.translation().Z + velocity.Z);
rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime * abs(velocity.Z) * 100);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime * abs(velocity.X) * 100);
m_Rotation = (rotX*rotZ);
m_Ball = (m_Ball.invert() * m_Rotation).invert();
m_Ball.m03 = newPos.X;
m_Ball.m13 = newPos.Y;
m_Ball.m23 = newPos.Z;
This is the solution I came up with after reading this link provided by #Spektre. Basically you just invert the ModelMatrix of the ball to get it into world position, do your rotation and then transform it back into local space.
You have to set the newPos Vector before you rotate, otherwise it would affect future transformations.

Rotation: Quaternion to matrix

I am trying to display a 360 panorama using an IMU for head tracking.
Yaw works correctly but the roll and pitch are reverse. I also notice that the pitch contains some roll (and maybe vice-versa).
I am receiving (W, X, Y, Z) coordinate from the IMU that I am storing in an array as X, Y, Z, W.
The next step is converting the quaternion to a rotation matrix. I have looked at many examples, and can't seem to find anything wrong with the following code:
static GLfloat rotation[16];
// Quaternion (x, y, z, w)
static void quaternionToRotation(float* quaternion)
{
// Normalize quaternion
float magnitude = sqrt(quaternion[0] * quaternion[0] +
quaternion[1] * quaternion[1] +
quaternion[2] * quaternion[2] +
quaternion[3] * quaternion[3]);
for (int i = 0; i < 4; ++i)
{
quaternion[i] /= magnitude;
}
double xx = quaternion[0] * quaternion[0], xy = quaternion[0] * quaternion[1],
xz = quaternion[0] * quaternion[2], xw = quaternion[0] * quaternion[3];
double yy = quaternion[1] * quaternion[1], yz = quaternion[1] * quaternion[2],
yw = quaternion[1] * quaternion[3];
double zz = quaternion[2] * quaternion[2], zw = quaternion[2] * quaternion[3];
// Column major order
rotation[0] = 1.0f - 2.0f * (yy + zz);
rotation[1] = 2.0f * (xy - zw);
rotation[2] = 2.0f * (xz + yw);
rotation[3] = 0;
rotation[4] = 2.0f * (xy + zw);
rotation[5] = 1.0f - 2.0f * (xx + zz);
rotation[6] = 2.0f * (yz - xw);
rotation[7] = 0;
rotation[8] = 2.0f * (xz - yw);
rotation[9] = 2.0f * (yz + xw);
rotation[10] = 1.0f - 2.0f * (xx + yy);
rotation[11] = 0;
rotation[12] = 0;
rotation[13] = 0;
rotation[14] = 0;
rotation[15] = 1;
}
The rotation matrix is then used in the draw call as such:
static void draw()
{
// Get IMU quaternion
float* quaternion = tracker.getTrackingData();
if (quaternion != NULL)
{
quaternionToRotation(quaternion);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
// TODO: Multiply initialRotation quaternion with IMU quaternion
glMultMatrixf(initialRotation); // Initial rotation to point forward
glMultMatrixf(rotation); // Rotation based on IMU
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
gluSphere(quad, 0.1, 50, 50);
glBindTexture(GL_TEXTURE_2D, 0);
glPopMatrix();
glFlush();
glutSwapBuffers();
}
I tried to set all but one fields in the quaternion to 0, and I notice that they all work individually, except roll and pitch is swapped around. I tried swapping X and Y but this does not seem to help.
Any help would be really appreciated. Please let me know as well if you have any steps that can let me debug my issue. Thanks!

OpenGL Camera rotation weirdness

I've been working on a small project with FreeGlut and Glew. Now I'm coding a camera system, but there are some things that are simply weird:
In fullscreen mode if the mouse moves in lower area of the screen, camera movements are faster than if camera moves in upper areas.
The camera makes weird movement, always in same direction, a small 8 figure move move.
code:
void MouseOps(int x, int y)
{
// Changes in mousepositions. Always same direction and
// in lower right corner of monitor faster, for some reason.
deltaX = x - MousePreviousX;
deltaY = y - MousePreviousY;
// Also I didn't bother to put * 360 in next equations,
// because it would make the camera jump for crazy.
// resx and resy are screen resolutions.
// Endresult should be that camera can
// rotate once when mouse moves over screen
yaw = yaw + (((deltaX / resx)) * deginrad);
pitch = pitch + (((deltaY / resy)) * deginrad);
//Check clippings (eg. camera wont end upside down etc.)
if(yaw >= (2 * pi) || yaw <= (-2 * pi) )
yaw = 0;
if(pitch >= (pi / 2))
pitch = pi / 2;
if(pitch <= (pi / -2))
pitch = pi / -2;
//Calculate x, y, and z coordinates of unit sphere to look at (r = 1)
cam_normX = cos(yaw) * sin(pitch);
cam_normY = sin(yaw) * sin(pitch);
cam_normZ = cos(yaw);
// Current x and y to previous
int MousePreviousX = x;
int MousePreviousY = y;
}
I tried to use this
http://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
system to calculate the point to look at. Then I passed all "cam_norm" variables to
gluLookAt(cam_posX, cam_posY, cam_posZ,
cam_posX+cam_normX, cam_posY+cam_normY, cam_posZ + cam_normZ,
cam_upX, cam_upY, cam_upZ);
I don't know why this works but it fixed all problems:
bool isCursorWarping = false;
void MouseOps(int x, int y)
{
if(isCursorWarping == false){
// Changes in mousepositions. Always same direction and in lower right corner of monitor faster, for some reason.
deltaX = x - MousePreviousX;
deltaY = y - MousePreviousY;
yaw = yaw + ((((deltaX / resx)) * deginrad) * 360);
pitch = pitch + ((((deltaY / resy)) * deginrad) * 360);
//Check clippings (eg. camera wont end upside down etc.)
if(x >= resx - 1 || y >= resy - 1 || x == 0 || y == 0)
{
warpCursor();
MousePreviousX = resx / 2;
MousePreviousY = resy / 2;
}else{
MousePreviousX = x;
MousePreviousY = y;
}
if(yaw >= (2 * pi) || yaw <= (-2 * pi) )
yaw = 0;
if(pitch >= (pi / 2))
pitch = pi / 2;
if(pitch <= (pi / -2))
pitch = pi / -2;
//Calculate x, y, and z coordinates of unit sphere to look at (r = 1)
cam_normX = cos(pitch) * cos(yaw);
cam_normY = sin(pitch) * sin(yaw);
cam_normZ = cos(pitch) * sin(yaw);
}
// Current x and y to previous and cleanup
isCursorWarping = false;
}
void warpCursor()
{
isCursorWarping = true;
glutWarpPointer(resx / 2, resy / 2);
}
Then I pass the "cam_norm" values to:
gluLookAt(0.0f, 1.0f, 2.0f, 0.0f + cam_normX, 1.0f + cam_normY, 2.0f+ cam_normZ, 0.0f, 0.1f, 0.0f);