OpenGL - 重新绑定 glBindVertexArray 不会在屏幕上绘制任何内容
OpenGL - rebinding glBindVertexArray doesn't draw anything to the screen
我刚开始尝试学习 opengl,在将代码的各个部分解耦为 classes 的过程中,我 运行 遇到了问题。
这是一个非常简单的问题。在下面的代码中,如果 Mesh 构造函数底部的 glBindVertexArray(0)
被注释掉,它会将图像绘制到屏幕上,但是如果我把它放在
中,则不会绘制图像。
在 Mesh.draw()
函数中调用 glDrawElements
之前有一个 glBindVertexArray(vao)
命令,所以看起来应该可以,但实际上没有。
主要代码:
int main(int argc, char*argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0)
std::cout << "SDL did not intizlize. SDL Error: " << SDL_GetError() << std::endl;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window* window = SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
SDL_GLContext context = SDL_GL_CreateContext(window);
glewExperimental = GL_TRUE;
GLenum glewTest = glewInit();
if (glewTest != GLEW_OK)
std::cout << glewGetErrorString(glewTest) << std::endl;
std::cout << glGetError() << std::endl;
Mesh mesh;
Shaders shaders("basicShader");
Texture texture("kitten.png");
//Loop stuff
bool quit = false;
SDL_Event e;
double frameCounter = 0;
double time = SDL_GetTicks();
while (!quit) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
quit = true;
}
if (e.type == SDL_KEYDOWN) {
if (e.key.keysym.sym == SDLK_ESCAPE)
quit = true;
}
}
++frameCounter;
if (SDL_GetTicks() - time >= 500) {
std::cout << "FPS: " << frameCounter / ((SDL_GetTicks() - time) / 1000) << std::endl;
frameCounter = 0;
time = SDL_GetTicks();
}
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
mesh.draw();
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Swap buffers
SDL_GL_SwapWindow(window);
}
mesh.~Mesh();
SDL_GL_DeleteContext(context);
SDL_Quit();
return 0;
}
网格class代码:
Mesh::Mesh() {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
-0.5f, 0.5f, 0.0, 0.0,
0.5f, 0.5f, 1.0, 0.0,
0.5f, -0.5f, 1.0, 1.0,
-0.5f, -0.5f, 0.0, 1.0
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
//glBindVertexArray(0);
}
void Mesh::draw() {
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glBindVertexArray(0);
}
Mesh::~Mesh() {
glDeleteVertexArrays(1, &vao);
}
我还有着色器设置代码未在此处发布,它调用 glVertexArrayPointer()
和 glEnableVertexAttribArray()
来设置顶点属性。
问题在于您在设置着色器时进行的 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用。这两个调用都修改了当前 VAO 的状态。
如果您致电:
glBindVertexArray(0);
在网格设置结束时,0 现在是您当前的 VAO(在兼容性配置文件中是合法的 VAO,但在核心配置文件中不是)。因此,您在此之后对 glVertexAttribPointer()
和 glEnableVertexAttribArray()
的调用现在将修改 VAO 0 中的状态。
当您在 draw()
方法开始时调用绑定 VAO 时:
glBindVertexArray(vao);
您正在使用网格 VAO 中的所有状态。这反过来意味着您在 VAO 0 中设置的状态未被使用。由于在绑定网格 VAO 时您从未进行过 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用,因此现在根本没有设置顶点属性。
你真的很幸运,当你没有在构造函数的末尾解除绑定 VAO 时它起作用了。由于在设置着色器时网格 VAO 仍处于绑定状态,因此 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用修改了网格 VAO 状态,从而产生了预期的结果。但是,如果您使用多个 mesh/shader.
,这很可能会惨败
您确实需要在设置网格时设置顶点状态,同时绑定特定网格的 VAO。以下调用都修改 VAO 状态,并且必须在绑定正确的 VAO 时进行:
glEnableVertexAttribArray(...)
glDisableVertexAttribArray(...)
glVertexAttribPointer(...)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)
在设置着色器程序时修改顶点设置状态在概念上也是有问题的。顶点设置状态不是着色器程序状态的一部分。
我刚开始尝试学习 opengl,在将代码的各个部分解耦为 classes 的过程中,我 运行 遇到了问题。
这是一个非常简单的问题。在下面的代码中,如果 Mesh 构造函数底部的 glBindVertexArray(0)
被注释掉,它会将图像绘制到屏幕上,但是如果我把它放在
在 Mesh.draw()
函数中调用 glDrawElements
之前有一个 glBindVertexArray(vao)
命令,所以看起来应该可以,但实际上没有。
主要代码:
int main(int argc, char*argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0)
std::cout << "SDL did not intizlize. SDL Error: " << SDL_GetError() << std::endl;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window* window = SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
SDL_GLContext context = SDL_GL_CreateContext(window);
glewExperimental = GL_TRUE;
GLenum glewTest = glewInit();
if (glewTest != GLEW_OK)
std::cout << glewGetErrorString(glewTest) << std::endl;
std::cout << glGetError() << std::endl;
Mesh mesh;
Shaders shaders("basicShader");
Texture texture("kitten.png");
//Loop stuff
bool quit = false;
SDL_Event e;
double frameCounter = 0;
double time = SDL_GetTicks();
while (!quit) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
quit = true;
}
if (e.type == SDL_KEYDOWN) {
if (e.key.keysym.sym == SDLK_ESCAPE)
quit = true;
}
}
++frameCounter;
if (SDL_GetTicks() - time >= 500) {
std::cout << "FPS: " << frameCounter / ((SDL_GetTicks() - time) / 1000) << std::endl;
frameCounter = 0;
time = SDL_GetTicks();
}
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
mesh.draw();
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Swap buffers
SDL_GL_SwapWindow(window);
}
mesh.~Mesh();
SDL_GL_DeleteContext(context);
SDL_Quit();
return 0;
}
网格class代码:
Mesh::Mesh() {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
-0.5f, 0.5f, 0.0, 0.0,
0.5f, 0.5f, 1.0, 0.0,
0.5f, -0.5f, 1.0, 1.0,
-0.5f, -0.5f, 0.0, 1.0
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
//glBindVertexArray(0);
}
void Mesh::draw() {
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glBindVertexArray(0);
}
Mesh::~Mesh() {
glDeleteVertexArrays(1, &vao);
}
我还有着色器设置代码未在此处发布,它调用 glVertexArrayPointer()
和 glEnableVertexAttribArray()
来设置顶点属性。
问题在于您在设置着色器时进行的 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用。这两个调用都修改了当前 VAO 的状态。
如果您致电:
glBindVertexArray(0);
在网格设置结束时,0 现在是您当前的 VAO(在兼容性配置文件中是合法的 VAO,但在核心配置文件中不是)。因此,您在此之后对 glVertexAttribPointer()
和 glEnableVertexAttribArray()
的调用现在将修改 VAO 0 中的状态。
当您在 draw()
方法开始时调用绑定 VAO 时:
glBindVertexArray(vao);
您正在使用网格 VAO 中的所有状态。这反过来意味着您在 VAO 0 中设置的状态未被使用。由于在绑定网格 VAO 时您从未进行过 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用,因此现在根本没有设置顶点属性。
你真的很幸运,当你没有在构造函数的末尾解除绑定 VAO 时它起作用了。由于在设置着色器时网格 VAO 仍处于绑定状态,因此 glVertexAttribPointer()
和 glEnableVertexAttribArray()
调用修改了网格 VAO 状态,从而产生了预期的结果。但是,如果您使用多个 mesh/shader.
您确实需要在设置网格时设置顶点状态,同时绑定特定网格的 VAO。以下调用都修改 VAO 状态,并且必须在绑定正确的 VAO 时进行:
glEnableVertexAttribArray(...)
glDisableVertexAttribArray(...)
glVertexAttribPointer(...)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)
在设置着色器程序时修改顶点设置状态在概念上也是有问题的。顶点设置状态不是着色器程序状态的一部分。