编写 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 基本摄像机运动的主要内容,如果未能解决你的问题,请参考以下文章

通过网络摄像头实时采集视频,采用OpenCV识别运动物体,实现有运动物体经过时存储,没有时则不存储.

OpenGL ES 2.0 卷绕和背面剪裁

太阳系Demo(openGL)

OpenGL相机旋转怪异

OpenGL ES 2.0 摄像机与投影

◮OpenGL-场景漫游(摄像机/键鼠控制)