【OpenGL学习】第2课:用OpenGL创建第一个窗口

仅自学做笔记用,后续有错误会更改

环境搭建

  1. 搭建流程参考文章:
    https://learnopengl-cn.github.io/01 Getting started/02 Creating a window/

  2. GLFW库

  • 什么是GLFW: GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文、定义窗口参数以及处理用户输入,对我们来说这就够了。(最流行的几个库有GLUT,SDL,SFML和GLFW)
  • GLFW库概括:一个轻量级的库,负责处理窗口创建、鼠标 / 键盘事件、上下文管理等与操作系统交互的工作(OpenGL 本身不处理窗口,必须依赖这类库)。
  • 下载链接:https://www.glfw.org/download.html
  • 编译: 最好是下载源码自己去编译,这样才能匹配你的系统。编译过后,我们需要的文件就是glfw3.lib和库的include文件。
  • 库的链接这里不多作描述,跟其他第三方库的链接是一致的。
  1. GLAD库
  • 什么是GLAD:因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异,在Windows上会是类似这样(你可以看到代码非常复杂,而且很繁琐,我们需要对每个可能使用的函数都要重复这个过程。幸运的是,有些库能简化此过程,其中GLAD是目前最新,也是最流行的库。):
c 复制代码
// 定义函数原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正确的函数并赋值给函数指针
GL_GENBUFFERS glGenBuffers  = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 现在函数可以被正常调用了
GLuint buffer;
glGenBuffers(1, &buffer);
  • GLAD库的作用概括:OpenGL 没有统一的函数加载方式(不同系统 / 显卡实现不同),GLAD 用于自动加载当前系统支持的 OpenGL 函数指针,是使用 OpenGL 的前提。

  • 配置GLAD库:GLAD是一个开源的库,它能解决我们上面提到的那个繁琐的问题。GLAD的配置与大多数的开源库有些许的不同,GLAD使用了一个在线服务。在这里我们能够告诉GLAD需要定义的OpenGL版本,并且根据这个版本加载所有相关的OpenGL函数。

    打开GLAD的在线服务,将语言(Language)设置为C/C++,在API选项中,选择3.4以上的OpenGL(gl)版本(我们的教程中将使用3.4版本,但更新的版本也能用)。之后将模式(Profile)设置为Core,并且保证选中了生成加载器(Generate a loader)选项。现在可以先(暂时)忽略扩展(Extensions)中的内容。都选择完之后,点击生成(Generate)按钮来生成库文件。

    GLAD现在应该提供给你了一个zip压缩文件,包含两个头文件目录,和一个glad.c文件。将两个头文件目录(glad和KHR)复制到你的Include文件夹中(或者增加一个额外的项目指向这些目录),并添加glad.c文件到你的工程中。

代码

  • 这里给出一个GLFW+GLAD显示一个窗口的示例
c 复制代码
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include <cstdio>
#include <iostream>

/********************函数声明********************/
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

/********************全局变量********************/
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

/********************入口********************/
int main()
{
    // GLFW初始化
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
    // GLFW窗口创建
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    // GLAD:加载所有OpenGL函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    // 渲染循环
    while (!glfwWindowShouldClose(window))
    {
        // 检查用户输入
        processInput(window);
        // 渲染指令(这里暂时把窗口渲染为蓝绿色,类似黑板)
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        // GLFW:交换颜色缓冲(双缓冲机制)
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    // glfw: 释放/删除之前的分配的所有资源
    glfwTerminate();
    //
    return 0;
}

/********************函数定义********************/
// 处理输入
void processInput(GLFWwindow* window)
{
    // 按下ESC键关闭窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, true);
    }
}
// 窗口大小改变回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // 确认视口与新窗口尺寸匹配;注意,在视网膜显示器上,宽度和高度将显著大于指定值。
    // 高度和宽度会根据你的显示器分辨率进行调整
    glViewport(0, 0, width, height);
}

代码解析

  • main函数(入口) 核心流程解析
      1. GLFW 初始化与窗口属性设置
c 复制代码
// 初始化GLFW库(必须第一步,否则后续GLFW函数无法使用)
glfwInit();
// 设置窗口相关的"提示参数"(告诉GLFW我们需要什么样的窗口)
// 指定OpenGL版本为3.4(主版本号3,次版本号4)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
// 指定使用"核心模式"(Core Profile):只包含现代OpenGL特性,移除旧版冗余功能
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 苹果系统特殊处理:苹果的OpenGL实现需要开启向前兼容模式
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

glfwWindowHint:用于配置后续创建的窗口属性,参数是 "键 - 值" 对。

例如:GLFW_CONTEXT_VERSION_MAJOR指定 OpenGL 主版本,3表示我们要使用 OpenGL 3.x。

核心模式(Core Profile):是现代 OpenGL 的推荐用法,避免使用已废弃的函数,减少兼容性问题。

      1. 创建窗口与上下文绑定
c 复制代码
// 创建窗口:参数依次为宽、高、窗口标题、全屏显示的显示器(NULL表示窗口模式)、共享上下文(NULL表示不共享)
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

// 检查窗口是否创建成功(失败可能因显卡不支持OpenGL 3.3或系统资源不足)
if (window == NULL)
{
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();  // 失败则释放GLFW资源
    return -1;
}

// 将窗口的OpenGL上下文设置为当前线程的"主上下文"
// (OpenGL需要一个"上下文"来存储状态,所有OpenGL操作都基于当前上下文)
glfwMakeContextCurrent(window);

// 设置窗口大小变化时的回调函数:当窗口被拉伸/缩放时,自动调用framebuffer_size_callback
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

OpenGL 上下文:可以理解为 OpenGL 的 "工作环境",存储了所有状态(如当前绑定的对象、渲染设置等)。

一个窗口对应一个上下文,必须通过glfwMakeContextCurrent将其设为当前上下文,后续 OpenGL 函数才能生效。

回调函数:glfwSetFramebufferSizeCallback注册一个函数,当窗口大小改变时,GLFW 会自动调用这个函数,我们可以在其中处理渲染区域的适配。

      1. 初始化 GLAD(加载 OpenGL 函数)
c 复制代码
// 初始化GLAD:通过GLFW提供的glfwGetProcAddress获取系统的OpenGL函数地址
// (GLAD需要知道如何加载函数,GLFW的这个函数可以帮我们做到)
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

关键作用:OpenGL 函数在编译时无法确定地址(不同系统 / 显卡实现不同),必须在运行时动态加载。

GLAD 通过gladLoadGLLoader自动完成这个过程,加载成功后才能调用glClear等 OpenGL 函数。

      1. 渲染循环(程序主循环)
c 复制代码
// 渲染循环:只要窗口没有被要求关闭(点击关闭按钮或代码触发),就一直循环
while (!glfwWindowShouldClose(window))
{
    // 1. 处理用户输入(如键盘、鼠标)
    processInput(window);

    // 2. 渲染指令:这里简单渲染一个背景色
    // 设置清除颜色(RGBA格式,范围0.0-1.0):这里是深青灰色(0.2红, 0.3绿, 0.3蓝, 1.0透明)
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    // 用上面设置的颜色清除"颜色缓冲"(OpenGL的渲染结果先存在缓冲中,而不是直接显示)
    glClear(GL_COLOR_BUFFER_BIT);

    // 3. 交换缓冲并处理事件
    // 交换前后缓冲(双缓冲机制:前缓冲显示,后缓冲绘制,交换后避免画面闪烁)
    glfwSwapBuffers(window);
    // 处理所有待处理的事件(如鼠标移动、按键按下),并触发对应的回调函数
    glfwPollEvents();
}

渲染循环:是图形程序的核心,确保窗口持续显示并响应交互。

循环条件!glfwWindowShouldClose(window)表示:只要窗口没有被标记为 "应该关闭",就一直运行。

双缓冲机制: OpenGL 渲染时,结果先画在 "后缓冲"(不可见),完成后通过glfwSwapBuffers与 "前缓冲"(可见)交换,避免绘制过程中画面闪烁(如果直接画在可见缓冲,用户会看到半成品)。

glClear(GL_COLOR_BUFFER_BIT):清除颜色缓冲(用glClearColor设置的颜色填充),相当于 "刷新画布"。

      1. 程序退出清理
c 复制代码
// 释放GLFW分配的所有资源(窗口、上下文等),避免内存泄漏
glfwTerminate();
return 0;
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习