c++ - 如何在现代 Opengl 中添加 View 矩阵

标签 c++ opengl matrix vertex-shader glm-math

从一开始(有点):几个月来,我一直在使用固定功能管道、弃用的 OpenGL 功能,如 gluLookAt 和 glTranslate。在意识到我这样做之后,我现在正尝试从翻译开始更正我的代码。我有一个橙色三角形,工作正常。当我实现模型矩阵时,我能够按照我想要的方式移动我的三角形(大部分)。

重点:现在我正在尝试创建 View 矩阵和投影矩阵。我还没有开始投影矩阵。当我将 View 矩阵添加到我的顶点着色器时,我再也看不到我的三角形了。我真的不确定为什么。

我的顶点着色器:

#version 150 core
in vec2 position;
uniform mat4 model;
uniform mat4 view;
void main()
{
    gl_Position=view*model*vec4(position, 0.0, 1.0);
}

我的 C++ 代码部分:

glm::mat4 model=glm::mat4(1.f);
model=glm::translate(model,glm::vec3(0.5f,0.7f,0.f));
model=glm::scale(model,glm::vec3(1.f));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
glm::mat4 view=glm::lookAt(glm::vec3(-1,-1,-1),glm::vec3(0,0,0),glm::vec3(0,0,1));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));
bool runOnce=true;
while(!glfwWindowShouldClose(window)){ //While glfw window is not closed yet
    glClearColor(0.1f,0.1f,0.1f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//Clear buffers to preset values (Clears the screen each time)
    glDrawArrays(GL_TRIANGLES,0,3);//Draw array with _,_,x vertices
    if(runOnce){
      model=glm::rotate(model,glm::radians(1200.f),glm::vec3(0.f,0.f,1.f));
      view=glm::lookAt(
        glm::vec3(0.0f, 0.f, 0.f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 0.0f, 1.0f)
      );
    }
    runOnce=false;
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));
    glfwSwapBuffers(window);//Swaps buffers (vertex data that is loaded to the screen by the gpu)
    glfwPollEvents();//glfwPollEvents, processes only those events (like keypresses) that have already been received and then returns immediately
}

如果我需要添加更多信息,我很乐意这样做。添加投影矩阵并没有解决问题。

我的整个 C++ 代码:

//Load functions from usr/include/
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>
//Main function
int main() {
    std::ios_base::sync_with_stdio(false);//Disables the synchronization between the C and C++ standard streams (though  mixing C- and C++ style I/O will be a challenge)
    std::cin.tie(NULL);//Unties cin from cout for faster cin and cout functions (this does mean that they are off sync but if you are not using cin than it doesnt matter)
    if(!glfwInit()) {
        //Initialization failed
        fprintf(stderr,"Failed to initialize GLFW\n");//Initialization error
        return -1;//A -1 returned to the main function indicates a unsuccessful run
    }
    //Window hints change the way the window works (like settings)
    glfwWindowHint(GLFW_SAMPLES,4);//4x antialiasing
    //"Major" and "minor" are two components of a single version number, separated by a dot.
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);//Version 3...
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);//.3
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);//To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);//We don't want the old OpenGL
    const GLFWvidmode*mode=glfwGetVideoMode(glfwGetPrimaryMonitor());//Constant (not-changable) GLFWvideomode (variable type) is named "mode", is set to the primary monitor
    glfwWindowHint(GLFW_RED_BITS,mode->redBits);
    glfwWindowHint(GLFW_GREEN_BITS,mode->greenBits);
    glfwWindowHint(GLFW_BLUE_BITS,mode->blueBits);
    glfwWindowHint(GLFW_REFRESH_RATE,mode->refreshRate);
    GLFWwindow*window=glfwCreateWindow(mode->width,mode->height,"My Title",glfwGetPrimaryMonitor(),NULL);//Create window that is the size of "mode" (constant primary monitor), named "", on the primary monitor
    if(window==NULL) { //If glfw window does not exist
        fprintf(stderr,"Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible.\n");
        glfwTerminate();//End glfw processes
        return -1;
    }
    glfwMakeContextCurrent(window);//Initialize GLEW
    glewExperimental=true;//Needed in core profile
    if(glewInit()!=GLEW_OK) { //If glew is not okay
        fprintf(stderr,"Failed to initialize GLEW\n");
        return -1;
    }
    glViewport(0,0,mode->width,mode->height);
    glEnable(GL_DEPTH_TEST);
    static const GLfloat g_vertex_buffer_data[]= { //Creates static (only runs once) constant (not-changable) array for VBO data
        -1.0f,-1.0f,0.0f, // triangle 1 : begin
        -1.0f,-1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f, // triangle 1 : end
    };
    GLuint vao;//Declares variable vbo, GLuint (opengl's unsigned interger)
    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);
    //Upload this vertex data to the graphics card
    GLuint vbo;//Declares variable vbo, GLuint (opengl's unsigned interger)
    glGenBuffers(1,&vbo);//Generate 1 buffer
    glBindBuffer(GL_ARRAY_BUFFER,vbo);//To upload the actual data to the graphics card you first have to make it the active object
    glBufferData(GL_ARRAY_BUFFER,sizeof(g_vertex_buffer_data),g_vertex_buffer_data,GL_STATIC_DRAW);//Now that our VBO is active we can copy the vertex data to it
    const char*vertexSource=R"glsl(
    #version 150 core
    in vec2 position;
    uniform mat4 model;
    uniform mat4 view;
    unifrom mat4 project;
    void main()
    {
        gl_Position=project*view*model*vec4(position, 0.0, 1.0);
    }
  )glsl";
    GLuint vertexShader=glCreateShader(GL_VERTEX_SHADER);//Creates shader object
    glShaderSource(vertexShader,1,&vertexSource,NULL);//Loads data to shader object
    glCompileShader(vertexShader);//Compiles shader object
    //Vertex compile error message
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    const char* fragmentSource=R"glsl(
    #version 150 core

    out vec4 outColor;

    void main()
    {
      outColor = vec4(1.0, 0.5, 0.0, 1.0);
    }
  )glsl";
    //The fragment shader is compiled in exactly the same way
    GLuint fragmentShader=glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader,1,&fragmentSource,NULL);
    glCompileShader(fragmentShader);
    //Fragment compile error message
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    //Combines shaders into one program
    GLuint shaderProgram=glCreateProgram();//Create shader program
    //Attatch vertex shader and fragment shader to new shader program
    glAttachShader(shaderProgram,vertexShader);
    glAttachShader(shaderProgram,fragmentShader);
    glLinkProgram(shaderProgram);//Links shader program
    //Shader program linking error message
    GLint linked;
    int InfoLogLength;
    glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (InfoLogLength > 0) {
        std::vector<char> ProgramErrorMessage(InfoLogLength+1);
        glGetProgramInfoLog(shaderProgram, InfoLogLength, NULL, &ProgramErrorMessage[0]);
        printf("%s\n", &ProgramErrorMessage[0]);
    }
    //To actually start using the shaders in the program, you just have to call:
    glUseProgram(shaderProgram);
    //You first need to retrieve a reference to the position input in the vertex shader
    GLint posAttrib=glGetAttribLocation(shaderProgram,"position");
    glVertexAttribPointer(posAttrib,2,GL_FLOAT,GL_FALSE,0,0);//Specify how the data for that input is retrieved from the array
    glEnableVertexAttribArray(posAttrib);
    glm::mat4 model=glm::mat4(1.f);
    model=glm::translate(model,glm::vec3(0.5f,0.7f,0.f));
    model=glm::scale(model,glm::vec3(1.f));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
    glm::mat4 view;//=glm::lookAt(glm::vec3(-1,-1,-1),glm::vec3(0,0,0),glm::vec3(0,0,1));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));
    glm::mat4 project = glm::ortho( -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 10.0f );
    int prj_loc = glGetUniformLocation(shaderProgram,"project");
    glUniformMatrix4fv(prj_loc ,1,GL_FALSE,glm::value_ptr(project));
    bool runOnce=true;
    while(!glfwWindowShouldClose(window)){ //While glfw window is not closed yet
        glClearColor(0.1f,0.1f,0.1f,1.0f);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//Clear buffers to preset values (Clears the screen each time)
        glDrawArrays(GL_TRIANGLES,0,3);//Draw array with _,_,x vertices
        if(runOnce){
          model=glm::rotate(model,glm::radians(1200.f),glm::vec3(0.f,0.f,1.f));
          view=glm::lookAt(
            glm::vec3(0.0f, 0.f, 0.f),
            glm::vec3(0.0f, 0.0f, 0.0f),
            glm::vec3(0.0f, 0.0f, 1.0f)
          );
        }
        runOnce=false;
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));
        glfwSwapBuffers(window);//Swaps buffers (vertex data that is loaded to the screen by the gpu)
        glfwPollEvents();//glfwPollEvents, processes only those events (like keypresses) that have already been received and then returns immediately
    }
    glfwTerminate();
    return 0;//A 0 returned to the main function indicates a successful run
}

我不确定它是否有帮助,但我在 chromebook 上使用 xfce4 Crouton。

最佳答案

When I add the view matrix to my vertex shader, I cannot see my triangle anymore. I am really not sure why.

您不使用投影矩阵。这意味着剪辑空间等于规范化的设备空间。 NDC 在 [-1, 1] 范围内(对于 x、y 和 z)。所有在 NDC 之外的几何图形都被裁剪。
这会导致您的三角形被远平面剪裁。请注意,由于您没有设置投影矩阵,近平面和远平面分别为 -1 和 1,因为归一化设备空间在 [-1, 1] 范围内。

要解决您的问题,请将投影矩阵添加到顶点着色器:

#version 150 core

in vec2 position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 project;

void main()
{
    gl_Position = project* view * model * vec4(position, 0.0, 1.0);
}

并设置一个带有自适应远平面的正交投影:

glm::mat4 project = glm::ortho( -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 10.0f );

int prj_loc = glGetUniformLocation(shaderProgram, "project");
glUniformMatrix4fv(prj_loc, 1, GL_FALSE, glm::value_ptr(project));

当然你也可以使用透视投影,就像这样:

glm::mat4 project = glm::perspective( glm::radians(90.0f), 1.0f, 0.1f, 10.0f );


进一步注意, View 矩阵的第二个定义将不起作用,因为眼睛位置和中心位置相等。改成

if(runOnce){
    view=glm::lookAt(
        glm::vec3(0.0f, 0.0f, 1.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
     );
} 

请注意,由于您的三角形是在 xy 平面中绘制的,因此眼睛位置应该具有 z 分量,因为如果视线在 xy 平面中,那么三角形的投影将只是一条无限细的线。


这意味着您的程序必须看起来像这样:

glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model,glm::vec3(0.5f,0.7f,0.f));
model = glm::scale(model,glm::vec3(1.f));

glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
glm::mat4 view = glm::lookAt(glm::vec3(-1,-1,-1),glm::vec3(0,0,0),glm::vec3(0,0,1));
glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));

glm::mat4 project = glm::ortho( -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 10.0f );
int prj_loc = glGetUniformLocation(shaderProgram,"project");
glUniformMatrix4fv(prj_loc,1,GL_FALSE,glm::value_ptr(project));

bool runOnce=true;
while(!glfwWindowShouldClose(window)){ 
    glClearColor(0.1f,0.1f,0.1f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES,0,3);

    if(runOnce){
      model=glm::rotate(model,glm::radians(1200.f),glm::vec3(0.f,0.f,1.f));
      view=glm::lookAt(
        glm::vec3(0.0f, 0.0f, 1.0f),
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(0.0f, 1.0f, 0.0f)
      );
    }
    runOnce=false;
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"model"),1,GL_FALSE,glm::value_ptr(model));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram,"view"),1,GL_FALSE,glm::value_ptr(view));

    glfwSwapBuffers(window);
    glfwPollEvents();
}


还有很多其他问题:

三角形的 2 个坐标相等。改成

static const GLfloat g_vertex_buffer_data[]= {
    -1.0f, -1.0f,  0.0f,
     1.0f, -1.0f,  0.0f,
     0.0f,  1.0f,  0.0f,
};

你的顶点坐标由 3 个分量组成,所以它必须是:

glVertexAttribPointer(
    posAttrib,
    3, // <--------  3 instead of 2
    GL_FLOAT,GL_FALSE,0,0);

在顶点着色器中有一个拼写错误,它必须是 uniform 而不是 unifrom:

uniform mat4 project;

关于c++ - 如何在现代 Opengl 中添加 View 矩阵,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49468877/

相关文章:

opengl - gluOrtho2D 和 glViewport

c++ - 涉及 char 和 if 语句的快速帮助

c++ - 使用 Qt 工具栏和操作释放分配的内存

c++ - 带有指针容器的自动类型推导

ios - 如何通过颜色混合着色器将 RGBA 转换为 BGRA

c++ - 如何将 Eigen::Quaternion<float> 转换为 Matrix4f?

c++ - std::pair: 过于严格的构造函数?

c++ - OpenGL:地形未绘制(高度图)

java - 我如何在Java中加入两个矩阵

c++ - Eigen :如何检查矩阵是否可逆