【发布时间】:2020-12-06 23:36:47
【问题描述】:
我用相机创建了一个简单的 3D 立方体。我正在尝试使用键盘修改器和鼠标输入来实现立方体对象的缩放、环绕和平移。此功能的要求如下:
- 按住 ALT 和鼠标左键同时向左移动鼠标可向左平移或向右平移以向右平移。
- 在上下移动鼠标时按住 ALT 和 LEFT 鼠标键将垂直环绕立方体。
- 在向上移动鼠标时按住 ALT 和鼠标右键将放大,向下移动鼠标将缩小。
这些事件的功能确实有效。
但是,存在以下问题:
- 只有在按住 ALT、单击并释放鼠标按钮后立方体才会更新。我希望能够连续渲染图像。我认为它会提供更流畅的用户体验。
- 当程序第一次启动时,立方体似乎已接近,但一旦按下 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