OpenGL无法绘制三角形求助:按LearnOpenGL教程操作仅显示空窗口
绘制OpenGL三角形时窗口空白的问题排查与修复
问题根源分析
你的代码存在几个关键逻辑错误,导致三角形无法正常显示:
- VAO使用顺序错误:OpenGL核心模式下,必须先绑定VAO,再配置顶点属性指针(
glVertexAttribPointer)和启用顶点属性数组,否则这些配置不会被存储到VAO中,绘制时无法读取顶点数据。 - 绘制调用位置错误:你把
glDrawArrays放在了主循环之外,只执行了一次,且主循环内没有任何渲染逻辑,窗口无法持续更新显示内容。 - 主循环缺少必要步骤:主循环内没有清除颜色缓冲、重新绘制图形和交换前后缓冲的操作,窗口内容无法刷新。
- 着色器字符串冗余:着色器源码末尾的
\0是多余的,C语言中字符串字面量会自动添加终止符,手动添加可能导致编译问题。
修正后的完整代码
主文件代码
#include <stdio.h> #include <stdlib.h> #include <glad/glad.h> #include <GLFW/glfw3.h> #define ASSERT(x, str) if (!(x)) {printf(str); exit(1);} #define WIDTH 800 #define HEIGHT 800 typedef unsigned int uint; typedef uint8_t u8; typedef uint16_t u16; typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef float f32; typedef double f64; typedef struct { GLFWwindow* window; } Canvas; Canvas canvas; f32 vertices[] = { 0.25f, 0.25f, 0.0f, 0.75f, 0.25f, 0.0f, 0.50f, 0.75f, 0.0f }; // 着色器源码 const char* vertex_shader_source = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main() {\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}"; const char* fragment_shader_source = "#version 330 core\n" "out vec4 color;\n" "void main() {\n" " color = vec4(1.0, 1.0, 1.0, 1.0);\n" "}"; void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); } int main() { // 初始化GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 创建窗口 canvas.window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL Triangle", NULL, NULL); ASSERT(canvas.window, "Failed to create GLFW window\n"); glfwMakeContextCurrent(canvas.window); ASSERT(gladLoadGLLoader((GLADloadproc)glfwGetProcAddress), "Failed to initialize GLAD\n"); glViewport(0, 0, WIDTH, HEIGHT); // 创建并绑定VAO(核心模式下必须先绑定VAO) uint VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); // 创建并绑定VBO,传入顶点数据 uint VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 配置顶点属性指针,此时配置会被存储到当前绑定的VAO中 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(f32), (void*)0); glEnableVertexAttribArray(0); // 编译顶点着色器 uint vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); glCompileShader(vertex_shader); i32 success; char infoLog[512]; glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertex_shader, 512, NULL, infoLog); printf("Vertex shader compilation failed:\n%s\n", infoLog); exit(1); } // 编译片段着色器 uint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); glCompileShader(fragment_shader); glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragment_shader, 512, NULL, infoLog); printf("Fragment shader compilation failed:\n%s\n", infoLog); exit(1); } // 创建并链接着色器程序 uint shader_program = glCreateProgram(); glAttachShader(shader_program, vertex_shader); glAttachShader(shader_program, fragment_shader); glLinkProgram(shader_program); glGetProgramiv(shader_program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader_program, 512, NULL, infoLog); printf("Shader program linking failed:\n%s\n", infoLog); exit(1); } // 删除已链接的着色器对象,不再需要 glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); // 主渲染循环 while(!glfwWindowShouldClose(canvas.window)) { // 处理输入 processInput(canvas.window); // 清除颜色缓冲(每次绘制前必须清除,否则会保留上一帧内容) glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 使用着色器程序 glUseProgram(shader_program); // 绑定VAO(绘制时只需要绑定VAO,VBO和顶点属性配置会被自动激活) glBindVertexArray(VAO); // 绘制三角形 glDrawArrays(GL_TRIANGLES, 0, 3); // 交换前后缓冲,处理事件 glfwSwapBuffers(canvas.window); glfwPollEvents(); } // 释放资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shader_program); glfwTerminate(); return 0; }
关键修正点说明
- 调整VAO创建顺序:先创建并绑定VAO,再进行VBO绑定和顶点属性配置,确保这些状态被正确存储到VAO中。
- 将渲染逻辑移入主循环:
glClear、glUseProgram、glBindVertexArray、glDrawArrays和glfwSwapBuffers都放入主循环,保证每一帧都重新绘制并刷新窗口。 - 添加颜色缓冲清除:
glClearColor设置背景色,glClear(GL_COLOR_BUFFER_BIT)清除上一帧内容,避免画面残留。 - 优化着色器字符串:移除多余的
\0,使用正确的换行符拼接字符串。 - 添加详细错误日志:扩展ASSERT逻辑,获取着色器编译和链接的错误日志,方便后续排查问题。
内容的提问来源于stack exchange,提问作者alaanvv
相关产品推荐
相关产品推荐

