c++ - 为什么使用 glDrawElements() 会导致段错误?

标签 c++ opengl glsl gldrawarrays

我正在学习 OpenGL,我使用 this guide 在 C++ 中编写了以下代码和 this video . 我还使用 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);
}

Shader.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);

};

Shader.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;
}

最佳答案

问题出在您的索引缓冲区绑定(bind)上。索引缓冲区 (GL_ELEMENT_ARRAY_BUFFER) 绑定(bind)是 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 绑定(bind)是 VAO 状态的一部分,因此当您调用时,当前索引缓冲区绑定(bind)将替换为 VAO 状态中的索引缓冲区绑定(bind):

glBindVertexArray(vao);     

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

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

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

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

关于c++ - 为什么使用 glDrawElements() 会导致段错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32407519/

相关文章:

c++ - 法线贴图和平移扰乱了我的照明

c++ - 在从抽象基类继承的类中添加方法?

c++ - 生成python绑定(bind),使用什么方法/程序

c++ - 读取文件并根据该信息制作对象 vector

opengl - 为什么在调用 glfwWindowHint() 后,glewInit() 会导致 GL_INVALID_ENUM

opengl - 如何将 ShaderToy 移植到独立 OpenGL

c++ - 长 double 的加权概率

c++ - 无法使用 DSA 加载纹理

java - 使用 libgdx 的 PolygonRegion/PolygonSprite 时出现奇怪、不一致和故障的结果

c++ - FreeType - Texture Atlas - 为什么我的文本呈现为四边形?