You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修正点说明

  1. 调整VAO创建顺序:先创建并绑定VAO,再进行VBO绑定和顶点属性配置,确保这些状态被正确存储到VAO中。
  2. 将渲染逻辑移入主循环:glClear、glUseProgram、glBindVertexArray、glDrawArrays和glfwSwapBuffers都放入主循环,保证每一帧都重新绘制并刷新窗口。
  3. 添加颜色缓冲清除:glClearColor设置背景色,glClear(GL_COLOR_BUFFER_BIT)清除上一帧内容,避免画面残留。
  4. 优化着色器字符串:移除多余的\0,使用正确的换行符拼接字符串。
  5. 添加详细错误日志:扩展ASSERT逻辑,获取着色器编译和链接的错误日志,方便后续排查问题。

内容的提问来源于stack exchange,提问作者alaanvv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:17:37