Search code examples
c++openglcameraglfw

How to make C++ OpenGL Camera look left and right?


In this project, I made a 3d cube and added a camera. We can go forward, backward, right or left with the W, A, S, D keys, but we cannot turn right or left. For example, I want to see the back side of the cube, but I cannot see it.Actually this is a simple problem and its solution is simple but my math level is insufficient, it will be better if someone explain it to me.I'll tell you my code and what I've tried below.

#include <Windows.h>

#include <gl/GL.h>
#include <gl/GLU.h>
#include <GLFW/glfw3.h>

#include <cstdio>
#include <iostream>
#include <cmath>
#include <math.h>

int width = 1280;
int height = 720;

float camera_z = 5;
float camera_y = 0;
float camera_x = 0;

float fov = 60;

GLFWwindow* window;

float speed = 0.01;


GLfloat vertices[] = {
    -1, -1, -1,   -1, -1,  1,   -1,  1,  1,   -1,  1, -1,
    1, -1, -1,    1, -1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1, -1,  1,    1, -1,  1,    1, -1, -1,
    -1,  1, -1,   -1,  1,  1,    1,  1,  1,    1,  1, -1,
    -1, -1, -1,   -1,  1, -1,    1,  1, -1,    1, -1, -1,
    -1, -1,  1,   -1,  1,  1,    1,  1,  1,    1, -1,  1
};

GLfloat colors[] = {
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   1, 1, 0,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0,
    1, 1, 0,   0, 1, 1,   0, 1, 0,   0, 1, 0
};



void keyboard() {

    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {

        glfwSetWindowShouldClose(window, GL_TRUE);

    }
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {

        camera_z = camera_z - speed;

    }
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {

        camera_x = camera_x - speed;

    }
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {

        camera_z = camera_z + speed;

    }
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {

        camera_x = camera_x + speed;
    }
    if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) {

        camera_y = camera_y + speed;
    }
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {

        camera_y = camera_y - speed;
    }

    

    if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {

        // need help
       
    }
    if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
       
        // need help
    }

   
}
       




void drawCube() {

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glColorPointer(3, GL_FLOAT, 0, colors);
    
    glDrawArrays(GL_QUADS, 0, 24);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);

}



int main(void)
{
    

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(width, height, "C++ OpenGL ", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    

    glEnable(GL_DEPTH_TEST); 

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        glViewport(0, 0, width, height);

        /* Render here */
        glClearColor(0.0, 192/256, 1, 1.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        float aspect = (float)width / (float)height;
        float fov = 60;
        gluPerspective(fov, aspect, 0.1, 1000);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        keyboard();
        
                                                      
        gluLookAt(
            camera_x,
            camera_y,
            camera_z,
            camera_x + 1, // need help
            camera_y + 1, // need help
            camera_z - fov ,
            0,
            1, 
            0
        );

        glTranslatef(0, 0,-3);
        drawCube();



        glFlush();

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

The things I tried were the variables camera_eyex,*_eyey,*_eyez. I added them to the loop to give the camera + 1 value. I used to increase or decrease them when I wanted to go left or right, but there are many errors in this method. For example, even if the camera rotates 45 degrees when I press the W key, it goes straight, so it does not go where I am looking. Also, turning more than 90 degrees to the right or left It's not possible. Also, when I press the left button to go left, turning is getting slower and slower.


Solution

  • You need to build a transform matrix. A transformation usually is a combination of translation, scale and rotation.

    Where you first rotate, then scale and then translate (the actual order of calculation - multiplication - is reversed):

    translation x scale x rotation
    

    If you want to scale or rotate around a certain point (pivot or center), then you have to translate to the center point and at the end translate back to the origin, like:

    translation x center x scale x rotation x -center
    

    The lookAt algorithm sets the rotation and translation based on the parameters (eye, target, up), whereas your goal is to separate the rotation from the translation. Therefore you have to build your own transformation, e.g.:

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    //set the position of the camera
    glTranslate(pos);
    
    //set the scale or rotation pivot
    //glTranslate(center);
    
    //scale
    //glScale(scale);
    
    //rotate around z-axis first
    glRotate(z_angle, 0, 0, 1);    
    
    //rotate around y-axis
    glRotate(y_angle, 0, 1, 0);
    
    //rotate around x-axis
    glRotate(x_angle, 1, 0, 0);
    
    //set the center back, if set before
    //glTranslate(-center);
    

    You'll find more info here (OpenGl related, although 'modern' OpenGl): https://learnopengl.com/Getting-started/Transformations

    Note: You probably have to adjust the input 'W A S D' to the changed axises. If, for example the 'W' key adjusts the z-component, you will probably not go 'forward' (along the z-axis) with the setup above. In order to move according to the transformed axises, you'll need the transformation matrix and extract them. Better is to use a math library, e.g. glm. How a orbit camera could be implemented, again, have a look at: https://learnopengl.com/Getting-started/Camera


    Building a camera system, needs some theoretical background in linear algebra.

    Based on the comment section below, i list all relevant links here:

    On top of that, it can be overwhelming for those who never visited a course in linear algebra, therefore i highly recommend The Essence of linear algebra by 3Blue1Brown.

    A note on matrix multiplication, what is not adequate emphasized and mostly overlooked: Once one has internalized the geometric meaning of a dot product, and the geometric meaning of the rows and columns of a matrix, one should also note that each component of the resulting matrix is the result of the dot product of a row and a column vector, visualize that and internalize it.