2

我正在学习 OpenGL,我使用本指南本视频在 C++ 中编写了以下代码。我也使用 GLFW 来创建上下文,使用 GLEW 来创建 GL 函数 大多数 Shader 类都是从视频上复制的,

问题是使用 glDrawElements() 在主循环内渲染会导致分段错误:

Segmentation fault


------------------
(program exited with code: 139)
Press return to continue

而使用 glDrawArrays() 我可以毫无问题地绘制。

有谁知道这可能是由什么引起的?我认为该错误可能取决于 Shader 类的实现,因为我在其他未使用此类并且关心主函数中的着色器的程序中使用了 glDrawArrays()。

程序.cpp

//INCLUDE AND DECLARATIONS
#include <iostream>
#include <fstream> 
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include "Shader.h"

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
unsigned long getFileLength(std::ifstream& file);
int loadshader(char* filename, GLchar** ShaderSource, unsigned long* len);

const GLuint WIDTH = 800, HEIGHT = 600;


//VERTEX DATA
float data[] = {
//    X      Y     R     G     B   
    -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, // Top-left
     0.5f,  0.5f, 0.0f, 1.0f, 0.0f, // Top-right
     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right
    -0.5f, -0.5f, 1.0f, 1.0f, 1.0f  // Bottom-left
};

 GLuint elements[] = {
        0, 1, 2,
        2, 3, 0
    };

//main
int main()
{   //INIT GLFW AND WINDOW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glewExperimental = GL_TRUE;
    glewInit();
    glViewport(0, 0, WIDTH, HEIGHT);



    //ALLOCATE BUFFERS
        //VERTEX ARRAY BUFFER
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
        //ELEMENT ARRAY BUFFER
    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
        //CREATE SHADER
    Shader shader("./shaders/basicShader");


    // main loop
    while (!glfwWindowShouldClose(window))
    {
        shader.Bind();
        glfwPollEvents();                       //window events
        glClearColor(1.0f, 0.0f, 0.5f, 0.5f);   //background
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);                //update window
    }

    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);
    glfwTerminate();
    return 0;
}


void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

着色器.h

#include <iostream>
#include <fstream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>


class Shader
{
        public:
            Shader(const std::string& filepath);
            ~Shader();
            void Bind();
        private:
            static const GLuint NUM_SHADERS = 2;
            GLuint program;  
            GLuint shaders[NUM_SHADERS];

            std::string LoadShader(const std::string& fileName);
            void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage);
            GLuint CreateShader(const std::string& text, unsigned int type);

};

着色器.cpp

#include "Shader.h"

Shader::Shader(const std::string& filepath)
{
    program = glCreateProgram();
    shaders[0] = CreateShader(LoadShader(filepath + ".vs"), GL_VERTEX_SHADER);
    shaders[1] = CreateShader(LoadShader(filepath + ".fs"), GL_FRAGMENT_SHADER);

    for(unsigned int i = 0; i < NUM_SHADERS; i++)
    {
        glAttachShader(program, shaders[i]);
    }
    glBindAttribLocation(program, 0, "position");
    glBindFragDataLocation(program, 0, "outColor");

    glLinkProgram(program);
    CheckShaderError(program, GL_LINK_STATUS, true, "Error linking shader program");

    glValidateProgram(program);
    CheckShaderError(program, GL_LINK_STATUS, true, "Invalid shader program");




    GLuint vao;
    glGenVertexArrays(1, &vao);     
    glBindVertexArray(vao);     

    GLint posAttrib = glGetAttribLocation(program, "position");
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
    glEnableVertexAttribArray(posAttrib);

    GLint AttribColor = glGetAttribLocation(program, "color");
    glVertexAttribPointer(AttribColor, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float)));
    glEnableVertexAttribArray(AttribColor);


}

Shader::~Shader()
{
    for(unsigned int i = 0; i < NUM_SHADERS; i++)
    {
        glDetachShader(program, shaders[i]);
        glDeleteShader(shaders[i]);
    }
    glDeleteProgram(program);
}

void Shader::Bind()
{
        glUseProgram(program);
}


//loads shaders from files
std::string Shader::LoadShader(const std::string& fileName)
{
    std::ifstream file;
    file.open((fileName).c_str());

    std::string output;
    std::string line;

    if(file.is_open())
    {
        while(file.good())
        {
            getline(file, line);
            output.append(line + "\n");
        }
    }
    else
    {
        std::cerr << "Unable to load shader: " << fileName << std::endl;
    }

    return output;
}
//Checks for eventual errors in shaders
void Shader::CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage)
{
    GLint success = 0;
    GLchar error[1024] = { 0 };

    if(isProgram)
        glGetProgramiv(shader, flag, &success);
    else
        glGetShaderiv(shader, flag, &success);

    if(success == GL_FALSE)
    {
        if(isProgram)
            glGetProgramInfoLog(shader, sizeof(error), NULL, error);
        else
            glGetShaderInfoLog(shader, sizeof(error), NULL, error);

        std::cerr << errorMessage << ": '" << error << "'" << std::endl;
    }
}

GLuint Shader::CreateShader(const std::string& text, unsigned int type)
{
    GLuint shader = glCreateShader(type);
        if(shader == 0)
            std::cerr << "error allocating shader" << std:: endl;

    const GLchar* p[1];
    p[0] = text.c_str();
    GLint lengths[1];
    lengths[0] = text.length();

    glShaderSource(shader, 1, p, lengths);
    glCompileShader(shader);
    CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error compiling shader!");

    return shader;
}
4

1 回答 1

6

问题在于您的索引缓冲区绑定。索引缓冲区 ( GL_ELEMENT_ARRAY_BUFFER) 绑定是 VAO 状态的一部分。跳过不相关的调用,您有以下总体顺序:

...
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
...
GLuint vao;
glGenVertexArrays(1, &vao);     
glBindVertexArray(vao);     
...
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

由于GL_ELEMENT_ARRAY_BUFFER绑定是 VAO 状态的一部分,因此当您调用时,当前索引缓冲区绑定将替换为 VAO 状态下的索引缓冲区绑定:

glBindVertexArray(vao);     

由于您在绑定 VAO 时从不绑定索引缓冲区,这意味着 VAO 状态下的索引缓冲区绑定为 0。因此,在此调用之后您没有绑定索引缓冲区。

接下来发生的是您glDrawElements()使用最后一个参数进行调用0。如果没有索引缓冲区绑定,最后一个参数将被解释为指向 CPU 内存的指针。所以 OpenGL 尝试读取地址 0 处的索引数据,从而导致崩溃。

要解决此问题,您只需在绑定 VAO 时绑定索引缓冲区。您可以通过更改调用顺序来做到这一点,并在设置索引缓冲区之前创建/绑定 VAO。或者你可以在设置顶点属性状态时再次绑定。例如,在Shader构造函数的最后,在glVertexAttribPointer()和 相关调用之后,添加以下调用:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
于 2015-09-05T01:17:49.623 回答