编写 OpenGL 基本摄像机运动
Posted
技术标签:
【中文标题】编写 OpenGL 基本摄像机运动【英文标题】:Writing OpenGL basic camera movement 【发布时间】:2021-07-20 20:42:26 【问题描述】:我正在尝试在我的代码中实现一个摄像头以在金字塔周围移动。具体来说,我需要: WASD 键:这些键用于控制前进、后退、左转和右转。 QE 键:这些键应该用于控制向上和向下移动。 鼠标光标:这应该用于更改相机的方向,以便它可以上下或左右查看。 鼠标滚动:这应该用于调整移动的速度,或相机在场景中移动的速度。 我的金字塔代码如下。谁能告诉我我需要添加什么???我是openGL和C++的初学者。任何事情都会有帮助!
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
// GLM Math Header inclusions
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std; // Standard namespace
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Unnamed namespace
namespace
const char* const WINDOW_TITLE = "Upside Down Pyramid!"; // title
// width and height
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
// mesh data
struct GLMesh
GLuint vao; // vertex array object
GLuint vbos[2]; // vertex buffer objects
GLuint nIndices; // Number of indices
;
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Triangle mesh data
GLMesh gMesh;
// Shader program
GLuint gProgramId;
/* User-defined Function prototypes to:
* initialize the program, set the window size,
* redraw graphics on the window when resized,
* and render graphics on the screen
*/
bool UInitialize(int, char* [], GLFWwindow** window);
void UResizeWindow(GLFWwindow* window, int width, int height);
void UProcessInput(GLFWwindow* window);
void UCreateMesh(GLMesh& mesh);
void UDestroyMesh(GLMesh& mesh);
void URender();
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void UDestroyShaderProgram(GLuint programId);
/* Vertex Shader Source Code*/
const GLchar* vertexShaderSource = GLSL(440,
layout(location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
layout(location = 1) in vec4 color; // Color data from Vertex Attrib Pointer 1
out vec4 vertexColor;
// 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); //coordinates
vertexColor = color;
);
const GLchar* fragmentShaderSource = GLSL(440,
in vec4 vertexColor;
out vec4 fragmentColor;
void main()
fragmentColor = vec4(vertexColor);
);
int main(int argc, char* argv[])
if (!UInitialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh
UCreateMesh(gMesh);
// Create the shader program
if (!UCreateShaderProgram(vertexShaderSource, fragmentShaderSource, gProgramId))
return EXIT_FAILURE;
// Sets the background color
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
// input
// -----
UProcessInput(gWindow);
URender();
glfwPollEvents();
// Release mesh data
UDestroyMesh(gMesh);
// Release shader program
UDestroyShaderProgram(gProgramId);
exit(EXIT_SUCCESS); //program successfull
// Initialize
bool UInitialize(int argc, char* argv[], GLFWwindow** window)
// GLFW: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// GLFW: window creation
// ---------------------
* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
if (*window == NULL)
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
glfwMakeContextCurrent(*window);
glfwSetFramebufferSizeCallback(*window, UResizeWindow);
// GLEW: initialize
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
std::cerr << glewGetErrorString(GlewInitResult) << std::endl;
return false;
cout << "INFO: OpenGL Version: " << glGetString(GL_VERSION) << endl;
return true;
// process all input
void UProcessInput(GLFWwindow* window)
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
void UResizeWindow(GLFWwindow* window, int width, int height)
glViewport(0, 0, width, height);
void URender()
// z-depth
glEnable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 1. Scales the object by 2
glm::mat4 scale = glm::scale(glm::vec3(2.0f, 2.0f, 2.0f));
// 2. Rotates shape by 360 degrees in the x axis
glm::mat4 rotation = glm::rotate(360.0f, glm::vec3(1.0, 1.0f, 1.0f));
// 3. Place object at the origin
glm::mat4 translation = glm::translate(glm::vec3(0.0f, 0.0f, 0.0f));
// Model matrix: transformations are applied right-to-left order
glm::mat4 model = translation * rotation * scale;
// Transforms the camera
glm::mat4 view = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f));
// Creates a orthographic projection
glm::mat4 projection = glm::ortho(-5.0f, 5.0f, -5.0f, 5.0f, 0.1f, 100.0f);
// Set the shader to be used
glUseProgram(gProgramId);
// Retrieves and passes transform matrices
GLint modelLoc = glGetUniformLocation(gProgramId, "model");
GLint viewLoc = glGetUniformLocation(gProgramId, "view");
GLint projLoc = glGetUniformLocation(gProgramId, "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));
// Activate the VBOs contained within the mesh's VAO
glBindVertexArray(gMesh.vao);
// Draws the triangles to create pyramid
glDrawElements(GL_TRIANGLES, gMesh.nIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
glBindVertexArray(0);
glfwSwapBuffers(gWindow); // Flips the the back buffer with the front buffer every frame.
// Implements the UCreateMesh
void UCreateMesh(GLMesh& mesh)
// color position
GLfloat verts[] =
// Vertex Positions // Colors
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, // Top
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Bottom
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, // Top
0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f, 1.0f, // Bottom
-0.5f, -0.5f, -0.5f, 0.2f, 0.2f, 0.5f, 1.0f, // Top
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 1.0f, 1.0f, // Bottom
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 1.0f, // Top
0.5f, -0.5f, 0.5f, 0.2f, 0.2f, 0.5f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Bottom
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 1.0f, // Top
0.5f, -0.5f, 0.5f, 0.2f, 0.2f, 0.5f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, // Bottom
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 1.0f, // Top
-0.5f, -0.5f, -0.5f, 0.2f, 0.2f, 0.5f, 1.0f, // Middle
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f // Bottom
;
// Index data to share position data
GLushort indices[] =
2, 1, 0, // Triangle 1
3, 1, 0, // Triangle 2
;
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
glGenVertexArrays(1, &mesh.vao);
glBindVertexArray(mesh.vao);
// Create 2 buffers
glGenBuffers(2, mesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
mesh.nIndices = sizeof(indices) / sizeof(indices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);
// Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
void UDestroyMesh(GLMesh& mesh)
glDeleteVertexArrays(1, &mesh.vao);
glDeleteBuffers(2, mesh.vbos);
//Shader function
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId)
// error report
int success = 0;
char infoLog[512];
programId = glCreateProgram();
// Create the vertex and fragment shader objects
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
// Retrieve the shader source
glShaderSource(vertexShaderId, 1, &vtxShaderSource, NULL);
glShaderSource(fragmentShaderId, 1, &fragShaderSource, NULL);
// compile and check for errors)
glCompileShader(vertexShaderId);
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
glGetShaderInfoLog(vertexShaderId, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
glCompileShader(fragmentShaderId);
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
glGetShaderInfoLog(fragmentShaderId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
// Compiled shaders
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId);
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
glGetProgramInfoLog(programId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
glUseProgram(programId);
return true;
void UDestroyShaderProgram(GLuint programId)
glDeleteProgram(programId);
【问题讨论】:
您是指“经典”fps 运动还是“围绕”金字塔(即轨道运动)? 您有什么具体问题吗?我们不知道您的程序中哪些有效,哪些无效。当然没有人会尝试执行你的代码,因为我们没有你的设置。 @Sebphil,我的意思是在金字塔周围 【参考方案1】:有多种方法可以在场景中移动相机(请注意,您不是在相机周围移动,而是通常在所有顶点上移动)。一种方法是使用 lookAt 方法。此方法返回一个旋转矩阵,您可以将其用作相机的视图矩阵。许多来源已经详细讨论了这种方法:
Writing a LookAt function https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/lookat-function https://learnopengl.com/Getting-started/Camera简单地说,您正在使用相机位置、相机目标(相机将看到的)和向上矢量来定义相机方向。您可以使用这三个向量来描述相机的方向。因此,您的视图矩阵将通过以下方式计算:
glm::mat4 view = glm::lookAt(position, target, up)
(而不是 glm::mat4 view = glm::translate(glm::vec3(0.0f, 0.0f, -5.0f))
)。如何生成这些矢量取决于您,因此您的相机的行为方式也取决于您自己。如果你想看看相机模型here is one的实现。
【讨论】:
以上是关于编写 OpenGL 基本摄像机运动的主要内容,如果未能解决你的问题,请参考以下文章