【问题标题】:Move camera in continuous/flowing way using keyboard and mouse?使用键盘和鼠标以连续/流动的方式移动相机?
【发布时间】:2020-12-06 23:36:47
【问题描述】:

我用相机创建了一个简单的 3D 立方体。我正在尝试使用键盘修改器和鼠标输入来实现立方体对象的缩放、环绕和平移。此功能的要求如下:

  • 按住 ALT 和鼠标左键同时向左移动鼠标可向左平移或向右平移以向右平移。
  • 在上下移动鼠标时按住 ALT 和 LEFT 鼠标键将垂直环绕立方体。
  • 在向上移动鼠标时按住 ALT 和鼠标右键将放大,向下移动鼠标将缩小。

这些事件的功能确实有效。

但是,存在以下问题:

  1. 只有在按住 ALT、单击并释放鼠标按钮后立方体才会更新。我希望能够连续渲染图像。我认为它会提供更流畅的用户体验。
  2. 当程序第一次启动时,立方体似乎已接近,但一旦按下 ALT 按钮,它就会重置到我想要的位置。

此代码所需的库如下: glu32,glew32,freeglut,opengl32

代码如下:

    // Header Inclusions
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <GL/glcorearb.h> // Used for glClear

// GLM Math Header inclusions
#include <glm/glm.hpp> // Located in TDM-GCC-64
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

using namespace std; // Standard namespace

#define WINDOW_TITLE "Modern OpenGL" // Window title macro

// Shader program macro
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version "\n" #Source
#endif

/* Variable declarations for shader, window size initialization,
 * buffer and array objects */
GLint shaderProgram, WindowWidth = 800, WindowHeight = 600;
GLuint VBO, VAO;

GLfloat cameraSpeed = 0.5f; // Movement speed per frame

GLfloat lastMouseX = 400, lastMouseY = 300; // Locks mouse cursor to center of screen
GLfloat mouseXOffset, mouseYOffset, yaw = 0.0f, pitch = 0.0f; // mouse offset, yaw, and pitch variables
GLfloat sensitivity = 0.01f; // Used for mouse camera rotation sensitivity
GLfloat zoom = 10.0f; // Zoom multiple set to 10.

bool mouseDetected = true; // Initially true when mouse movement is detected
bool leftMouseClicked = false; // Set to false until button is clicked
bool rightMouseClicked = false; // Set to false until button is clicked


/* Will store key pressed: Used to determine if orthogonal view should be used instead of 3D
* when the letter z is pressed down */
GLchar currentKey;

// Global vector declarations
glm::vec3 cameraPosition = glm::vec3(0.0f, 0.0f,  1.0f); // Initial camera position. Placed 5 units in z
glm::vec3 CameraUpY      = glm::vec3(0.0f, 1.0f,  0.0f); // Temporary y unit vector
glm::vec3 CameraForwardZ = glm::vec3(0.0f, 0.0f, -1.0f); // Temporary z unit vector
glm::vec3 front          = glm::vec3(0.0f, 0.0f,  0.0f); // Temp z unit vector for mouse

// Function prototypes
void UResizeWindow(int, int);
void URenderGraphics(void);
void UCreateShader(void);
void UCreateBuffers(void);
void UMouseMove(int x, int y);
void OnMouseClick(int button, int state, int x, int y);

// Vertex Shader Source Code
const GLchar * vertexShaderSource = GLSL(330,
    layout (location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
    layout (location = 1) in vec3 color; // Color data from Vertex Attrib Pointer 1

    out vec3 mobileColor; // variable to transfer color data to the fragment shader

    // Global variables for the transform matrices.
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

void main(){
    gl_Position = projection * view * model * vec4(position, 1.0f); // transform vertices to clip coordinates
    mobileColor = color; // references incoming color data
    }
);

// Fragment Shader Source Code
const GLchar * fragmentShaderSource = GLSL(330,
    in vec3 mobileColor; // Variable to hold incoming color data from vertex shader

    out vec4 gpuColor; // Variable to pass color data to the GPU

    void main(){
        gpuColor = vec4(mobileColor, 1.0); // Sends color data to the GPU for rendering
    }
);

/* MAIN PROGRAM */
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutCreateWindow(WINDOW_TITLE);

    glutReshapeFunc(UResizeWindow);

    glewExperimental = GL_TRUE;
        if (glewInit() != GLEW_OK){
            std::cout << "Failed to initialize GLEW" << std::endl;
            return -1;
        }

    UCreateShader();
    UCreateBuffers();

    // Use the Shader program
    glUseProgram(shaderProgram);

    glClearColor(0.0f,0.0f, 0.0f, 1.0f); // set background color

    glutDisplayFunc(URenderGraphics);  // Renders graphics


    glutPassiveMotionFunc(UMouseMove); // Detects mouse movement

    glutMouseFunc(OnMouseClick); // Detects mouse button clicked

    glutMainLoop();

    // Destroys Buffer objects once used
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    return 0;
}

// Resize the window
void UResizeWindow(int w, int h){
    WindowWidth = w;
    WindowHeight = h;
    glViewport(0, 0, WindowWidth, WindowHeight);
}

// Renders graphics
void URenderGraphics(void){
    glEnable(GL_DEPTH_TEST); //Enable z-depth

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clears the screen

    glBindVertexArray(VAO); // Activate the Vertex Array Object before rendering and transforming them

    CameraForwardZ = front; // Replaces camera forward vector with Radians normalized as a unit vector

    // Transform the object
    glm::mat4 model;
    model = glm::translate(model, glm::vec3(0.0, 0.0f, 0.0f)); // Place the object at the center of the viewport
    model = glm::rotate(model, 45.0f, glm::vec3(0.0, 1.0f, 0.0f)); // Rotate the object 45 degrees on the X axis
    model = glm::scale(model, glm::vec3(2.0f, 2.0f, 2.0f)); // Increase the object size by a scale of 2

    // Transform the camera
    glm::mat4 view;
    view = glm::lookAt(CameraForwardZ, cameraPosition, CameraUpY);

    // Display perspective
    glm::mat4 projection;
    // Creates an perspective projection
    projection = glm::perspective(45.0f, (GLfloat)WindowWidth / (GLfloat)WindowHeight, 0.1f, 100.0f);

    // Retrieves and passes transform matrices to the shader program
    GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
    GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
    GLint projLoc = glGetUniformLocation(shaderProgram, "projection");

    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    glutPostRedisplay();

    // Draws the triangles
    glDrawArrays(GL_TRIANGLES, 0, 36);

    glBindVertexArray(0); // Deactivate the Vertex Array Object

    glutSwapBuffers(); // Flips the back buffer with the from buffer every frame. Similar to GL Flush.
}

// Creates the shader program
void UCreateShader(){
    // vertex shader
    GLint vertexShader = glCreateShader(GL_VERTEX_SHADER); // Create the vertex shader
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // Attaches the Vertex shader to the source code
    glCompileShader(vertexShader); // Compiles the Vertex shader

    // Fragment shader
    GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); // Create the fragment shader
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); // Attaches fragment shader to source code
    glCompileShader(fragmentShader); // Compiles the fragment shader

    // Shader program
    shaderProgram = glCreateProgram(); // Create the shader program and return an id
    glAttachShader(shaderProgram, vertexShader); // Attach Vertex shader to the shader program
    glAttachShader(shaderProgram, fragmentShader); // Attach fragment shader to the shader program
    glLinkProgram(shaderProgram); // Link vertex and fragment shader to shader program

    // Delete the vertex and fragment shader once linked
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
}

// Create the buffer and array objects
void UCreateBuffers(){
    // Position and color data
    GLfloat vertices[] = {
       // Vertex positions     // colors
               -0.5f, -0.5f, -0.5f,   1.0f, 0.0f, 0.0f,
                0.5f, -0.5f, -0.5f,   1.0f, 0.0f, 0.0f,
                0.5f,  0.5f, -0.5f,   1.0f, 0.0f, 0.0f,
                0.5f,  0.5f, -0.5f,   1.0f, 0.0f, 0.0f,
               -0.5f,  0.5f, -0.5f,   1.0f, 0.0f, 0.0f,
               -0.5f, -0.5f, -0.5f,   1.0f, 0.0f, 0.0f,

               -0.5f, -0.5f, 0.5f,    0.0f, 1.0f, 0.0f,
                0.5f, -0.5f, 0.5f,    0.0f, 1.0f, 0.0f,
                0.5f,  0.5f, 0.5f,    0.0f, 1.0f, 0.0f,
                0.5f,  0.5f, 0.5f,    0.0f, 1.0f, 0.0f,
               -0.5f,  0.5f, 0.5f,    0.0f, 1.0f, 0.0f,
               -0.5f, -0.5f, 0.5f,    0.0f, 1.0f, 0.0f,

               -0.5f,  0.5f,  0.5f,    0.0f, 0.0f, 1.0f,
               -0.5f,  0.5f, -0.5f,    0.0f, 0.0f, 1.0f,
               -0.5f, -0.5f, -0.5f,    0.0f, 0.0f, 1.0f,
               -0.5f, -0.5f, -0.5f,    0.0f, 0.0f, 1.0f,
               -0.5f, -0.5f,  0.5f,    0.0f, 0.0f, 1.0f,
               -0.5f,  0.5f,  0.5f,    0.0f, 0.0f, 1.0f,

                0.5f,  0.5f,  0.5f,    1.0f, 1.0f, 0.0f,
                0.5f,  0.5f, -0.5f,    1.0f, 1.0f, 0.0f,
                0.5f, -0.5f, -0.5f,    1.0f, 1.0f, 0.0f,
                0.5f, -0.5f, -0.5f,    1.0f, 1.0f, 0.0f,
                0.5f, -0.5f,  0.5f,    1.0f, 1.0f, 0.0f,
                0.5f,  0.5f,  0.5f,    1.0f, 1.0f, 0.0f,

               -0.5f, -0.5f, -0.5f,    0.0f, 1.0f, 1.0f,
                0.5f, -0.5f, -0.5f,    0.0f, 1.0f, 1.0f,
                0.5f, -0.5f,  0.5f,    0.0f, 1.0f, 1.0f,
                0.5f, -0.5f,  0.5f,    0.0f, 1.0f, 1.0f,
               -0.5f, -0.5f,  0.5f,    0.0f, 1.0f, 1.0f,
               -0.5f, -0.5f, -0.5f,    0.0f, 1.0f, 1.0f,

               -0.5f,  0.5f, -0.5f,    1.0f, 0.0f, 1.0f,
                0.5f,  0.5f, -0.5f,    1.0f, 0.0f, 1.0f,
                0.5f,  0.5f,  0.5f,    1.0f, 0.0f, 1.0f,
                0.5f,  0.5f,  0.5f,    1.0f, 0.0f, 1.0f,
               -0.5f,  0.5f,  0.5f,    1.0f, 0.0f, 1.0f,
               -0.5f,  0.5f, -0.5f,    1.0f, 0.0f, 1.0f
    };

    // Generate buffer Ids
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    // Activate the Vertex Array Object before binding and setting any VBO's and Vertex Attribute Pointers
    glBindVertexArray(VAO);

    // Activate the VBO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // Copy vertices to VBO

    // Set attribute pointer 0 to hold Position data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0); // Enables vertex attribute

    // Set attribute pointer 1 to hold Position data
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1); // Enables vertex attribute

    glBindVertexArray(0); // Deactivates the VAO which is good practice
}

// Implements the UMouseMove function
void UMouseMove(int x, int y) {
/* If mouse is clicked and left alt is held down then the cube
 * will be rotated in the direction of the mouse.*/
    if(glutGetModifiers() == GLUT_ACTIVE_ALT) {
       mouseXOffset = x - lastMouseX;
       mouseYOffset = lastMouseY - y;
       lastMouseX = x;
       lastMouseY = y;
       mouseXOffset *= sensitivity;
       mouseYOffset *= sensitivity;

       // Rotate Object if left mouse clicked
       while(leftMouseClicked) {
           yaw += mouseXOffset;
           pitch -= mouseYOffset;
           pitch = max(-0.5f, pitch);
           pitch = min(0.5f, pitch);
           leftMouseClicked = false; // resets leftMouseClicked to default state
       }

       // Zoom object if right mouse clicked
       while(rightMouseClicked) {
           zoom += mouseYOffset * cameraSpeed;  // Zoom into object
           rightMouseClicked = false; // resets rightMouseClicked to default state
       }

       front.x = zoom * cos(yaw);
       front.y = zoom * sin(pitch);
       front.z = sin(yaw) * cos(pitch) * zoom;
    }
       lastMouseX = x;
       lastMouseY = y;
}

// Check the mouse button click state to verify which button is pressed
void OnMouseClick(int button, int state, int x, int y){
    // Set leftMouseClicked State to true
    if(state == GLUT_DOWN && button == GLUT_LEFT_BUTTON){
        leftMouseClicked = true;
    }
    // Set rightMouseClicked State to true
    if(state == GLUT_DOWN && button == GLUT_RIGHT_BUTTON){
        rightMouseClicked = true;
    }
}

【问题讨论】:

  • 我对 GLUT 了解不多,也不确定是什么导致了您描述的具体问题,但您的代码中有几处我并不清楚。看起来您并没有在任何地方处理鼠标按钮向上事件,我希望这将是逻辑的重要部分。您似乎在UMouseMove() 中重复调用glutMouseFunc()。还有一些其他的事情。这是我的建议。创建一个新的测试项目,只专注于处理鼠标输入,使用日志来查看发生了什么。弄清楚这个逻辑,然后返回整个项目。
  • 我使用布尔变量作为触发器,而不是处理鼠标向上按钮。当鼠标按钮按下时,它被设置为 true,当它被按下时,它被设置回 false,这是默认值。即使当我处理鼠标向上事件时,移动仍然不稳定,并且不会随着鼠标移动而连续。仅在释放鼠标按钮后才会发生移动。
  • 也许我遗漏了一些东西,但逻辑对我来说似乎仍然是错误的。有人可能会按原样为您确定问题,但即使是一个基本的 OpenGL 程序也包含如此多的样板,以至于很难筛选。我仍然认为创建一个临时的、更简单的版本可能会有所帮助,该版本除了更改响应鼠标事件的值之外什么都不做,并在渲染函数中打印该值。然后您可以观察控制台并查看渲染函数是否正在运行以及值是否按预期更新。而且,如果您仍然需要帮助,您可以发布较小的示例。
  • @user9477548 LearnOpenGL - Camera
  • Understanding 4x4 homogenous transform matrices然后看看最后的链接有播放器和相机控制的例子(不同的风格和方法)

标签: c++ opengl input glut glm-math


【解决方案1】:

这里的问题是在主程序函数中使用glutPassiveMotionFunc。此函数仅在鼠标移动且未单击任何按钮时执行。 glutMouseFunc 每次按下和释放鼠标按钮时都会生成一个回调。这会触发仅将 leftMouseClicked 或 rightMouseClicked 的变量设置为 true 的函数“OnMouseClick”。在不再单击鼠标按钮(鼠标向上条件)之前,不会呈现任何内容。此时没有按下任何按钮,这会导致 glutPassiveMotionFunc 发送回调,触发“UMouseMove”功能。 “UMouseMove”函数然后计算 y 或 x 的变化并更新相机的前焦点。这就是为什么在按下鼠标按钮时对象不渲染的原因。仅当释放鼠标按钮(鼠标移动而没有按下按钮)时,才会更新坐标以进行渲染。

解决方案是使用glutMotionFunc 代替 glutPassiveMotionFunc。 glutMotionFunc 在单击鼠标按钮(鼠标向下位置)时鼠标移动时执行回调。这会在按下鼠标按钮的同时执行“UMouseMove”功能并同时更新相机的前焦点。 建议:

  1. 将 glutPassiveMotionFunc 改为 main 中的 glutMotionFunc 如下:

    glutDisplayFunc(URenderGraphics);  // Renders graphics
    
    glutMotionFunc(UMouseMove); // <--- Change this line //Detects mouse movement
    
    glutMouseFunc(OnMouseClick); // Detects mouse button clicked
    
    glutMainLoop();
    
  2. 编辑“UMouseMove”函数以首先检查是否按下了 ALT 键修饰符。删除在此方法中处理 leftMouseClicked 和 rightMouseClicked 变量。在 if 语句中移动该函数的最后两行,如下所示:

    // Implements the UMouseMove function
    void UMouseMove(int x, int y) {
      /* If mouse is clicked and left alt is held down then the cube
       * will be rotated in the direction of the mouse.*/
      if(glutGetModifiers() == GLUT_ACTIVE_ALT) {
         mouseXOffset = x - lastMouseX;
         mouseYOffset = lastMouseY - y;
         lastMouseX = x;
         lastMouseY = y;
         mouseXOffset *= sensitivity;
         mouseYOffset *= sensitivity;
    
         // Rotate Object if left mouse clicked
         if(leftMouseClicked) {
             yaw += mouseXOffset;
             pitch -= mouseYOffset;
             pitch = max(-0.5f, pitch);
             pitch = min(0.5f, pitch);
         }
    
         // Zoom object if right mouse clicked
         else if(rightMouseClicked) {
             zoom += mouseYOffset * cameraSpeed;  // Zoom into object
         }
    
         front.x = zoom * cos(yaw);
         front.y = zoom * sin(pitch);
         front.z = sin(yaw) * cos(pitch) * zoom;
    
         lastMouseX = x;
         lastMouseY = y;
      }
    }
    
  3. OnMouseClick 函数应处理所有鼠标按钮单击事件,以便进行良好的内务管理。这可以像下面这样简单:

    // Check the mouse button click state to verify which button is pressed
    void OnMouseClick(int button, int state, int x, int y){
      // Set leftMouseClicked State to true
      if(state == GLUT_DOWN && button == GLUT_LEFT_BUTTON){
        leftMouseClicked = true;
      }
      else{
        leftMouseClicked = false;
      }
    
      // Set rightMouseClicked State to true
      if(state == GLUT_DOWN && button == GLUT_RIGHT_BUTTON){
        rightMouseClicked = true;
      }
      else{
        rightMouseClicked = false;
      }
    }
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多