为什么使用 glDrawElements() 会导致分段错误?
Why does using glDrawElements() causes a Segmentation Fault?
我正在学习 OpenGL,我使用 this guide and this video 在 C++ 中编写了以下代码。
我还使用 GLFW 进行上下文创建,使用 GLEW 进行 GL 函数
大部分Shader class 是从链接的视频中复制的,
问题是在主循环内使用 glDrawElements() 进行渲染导致出现分段错误:
Segmentation fault
------------------
(program exited with code: 139)
Press return to continue
使用 glDrawArrays() 时我可以毫无问题地绘图。
有谁知道这可能是由什么引起的?
我认为错误可能取决于着色器 class 的实现,因为我在其他程序中使用了 glDrawArrays() 而没有使用它 class 并且关心主函数中的着色器。
program.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;
}
问题出在您的索引缓冲区绑定上。索引缓冲区 (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。因此,在此调用后您没有绑定索引缓冲区。
接下来发生的是您使用最后一个参数 0
进行 glDrawElements()
调用。如果没有索引缓冲区绑定,最后一个参数将被解释为指向 CPU 内存的指针。因此 OpenGL 尝试读取地址 0 处的索引数据,这导致了崩溃。
要解决此问题,您只需在绑定 VAO 时绑定索引缓冲区即可。您可以通过更改调用顺序和 create/bind 设置索引缓冲区之前的 VAO 来做到这一点。或者你可以在设置顶点属性状态的时候再绑定一次。例如,在 Shader
构造函数的最后,在 glVertexAttribPointer()
和相关调用之后,添加此调用:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
我正在学习 OpenGL,我使用 this guide and this video 在 C++ 中编写了以下代码。 我还使用 GLFW 进行上下文创建,使用 GLEW 进行 GL 函数 大部分Shader class 是从链接的视频中复制的,
问题是在主循环内使用 glDrawElements() 进行渲染导致出现分段错误:
Segmentation fault
------------------
(program exited with code: 139)
Press return to continue
使用 glDrawArrays() 时我可以毫无问题地绘图。
有谁知道这可能是由什么引起的? 我认为错误可能取决于着色器 class 的实现,因为我在其他程序中使用了 glDrawArrays() 而没有使用它 class 并且关心主函数中的着色器。
program.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;
}
问题出在您的索引缓冲区绑定上。索引缓冲区 (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。因此,在此调用后您没有绑定索引缓冲区。
接下来发生的是您使用最后一个参数 0
进行 glDrawElements()
调用。如果没有索引缓冲区绑定,最后一个参数将被解释为指向 CPU 内存的指针。因此 OpenGL 尝试读取地址 0 处的索引数据,这导致了崩溃。
要解决此问题,您只需在绑定 VAO 时绑定索引缓冲区即可。您可以通过更改调用顺序和 create/bind 设置索引缓冲区之前的 VAO 来做到这一点。或者你可以在设置顶点属性状态的时候再绑定一次。例如,在 Shader
构造函数的最后,在 glVertexAttribPointer()
和相关调用之后,添加此调用:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);