OpenGL 学习笔记 第一章:绘制一个窗口

OpenGL 学习笔记 第一章

前言

OpenGL 其实想学很久了,花了大概一个小时吧环境配置好,就准备开始学习了,那么我们开始进行开发旅程吧!

具体开发环境配置自行寻找教程

第一章:绘制一个窗口

我选择使用的开发的环境是Visual Studio 2026 现在改名为Visual Studio之前还有2022的标现在都是Visual Studio了,使用这种集成的IDE配置比较方便,如果使用VSCode的话需要自己写配置文件,这个就导致整个配置的周期非常长,我们使用Visual Studio 2026会节省很多事情,使用VSCode也是可以但是我们还是使用的2026,那么我们开发正式开始,我们这次简单绘制一个窗口

初始化GLFW

我们GLFW是一个窗口管理的包,我们需要通过它来管理我们的包,那么我们在创建一个窗口前需要初始化一下我们的GLFW

cpp 复制代码
    // 调用 glfwInit(); 以此来初始化我们的窗口 
    glfwInit();

    /// glfwWindowHint 配置我们的GLFW
    /// glfwWindowHint函数的第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择
    /// 第二个参数接受一个整型,用来设置这个选项的值
    /// OpenGL 3.3 因此我们的OpenGL的版本号设置为3.3
    /// 我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)。
    /// 明确告诉GLFW我们需要使用核心模式意味着我们只能使用OpenGL功能的一个子集(没有我们已不再需要的向后兼容特性)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

函数 glfwInit() 可以先进行我们的初步的初始化,我们还可以通过 glfwWindowHit()函数修改我们的配置文件,那么我们具体的需要修改两个参数,

  • 第一个参数:代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择
  • 第二个参数:接受一个整型,用来设置这个选项的值
MacOS 特殊情况

如果大家使用的操作系统是MacOS那么我们就需要添加下方的代码

复制代码
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
cpp 复制代码
    // 调用 glfwInit(); 以此来初始化我们的窗口 
    glfwInit();

    /// glfwWindowHint 配置我们的GLFW
    /// glfwWindowHint函数的第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择
    /// 第二个参数接受一个整型,用来设置这个选项的值
    /// OpenGL 3.3 因此我们的OpenGL的版本号设置为3.3
    /// 我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)。
    /// 明确告诉GLFW我们需要使用核心模式意味着我们只能使用OpenGL功能的一个子集(没有我们已不再需要的向后兼容特性)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    /// 如果你使用的是MacOS系统那么想要让我们的上面的配置文件生效就需要添加下面的代码
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

创建窗口

函数需要窗口的宽和高作为它的前两个参数。

第三个参数表示这个窗口的名称(标题),这里我们使用"LearnOpenGL",当然你也可以使用你喜欢的名称。

最后两个参数我们暂时忽略。

cpp 复制代码
    /// 创建窗口 GLFWWindow窗口 
    /// 函数需要窗口的宽和高作为它的前两个参数。
    /// 第三个参数表示这个窗口的名称(标题),这里我们使用"LearnOpenGL",当然你也可以使用你喜欢的名称。
    /// 最后两个参数我们暂时忽略。
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "OpenGL Template", NULL, NULL);
    if (window == NULL)
    {
        /// 一旦我们的窗口无法创建,我们就会报错
        std::cerr << "Failed to create GLFW window" << std::endl;
        // 关闭窗口
        glfwTerminate();
        return -1;
    }

设置上下文,将该窗口设置为上下文

复制代码
    /// 创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了。
    glfwMakeContextCurrent(window);

初始化GLAD

GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD

我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数

GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数

cpp 复制代码
    /// GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD
    /// 我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。
    /// GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

窗口管理函数

我的注释已经解释的非常清楚了,这边我就不做过多的解释

cpp 复制代码
/// <summary>
/// 我们同样也希望能够在GLFW中实现一些输入控制,这可以通过使用GLFW的几个输入函数来完成。
/// 我们将会使用GLFW的glfwGetKey函数,它需要一个窗口以及一个按键作为输入。
/// 这个函数将会返回这个按键是否正在被按下。我们将创建一个processInput函数来让所有的输入代码保持整洁
/// </summary>
/// <param name="window"></param>
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

/// <summary>
/// 然而,当用户改变窗口的大小的时候,视口也应该被调整。
/// 我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。这个回调函数的原型如
/// </summary>
/// <param name="window">窗口指针</param>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

循环绘制

我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。

我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。

这样我们才能实现一个游戏窗口渲染的效果。

cpp 复制代码
/// 我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。
/// 我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。
/// 因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。
/// 下面几行的代码就实现了一个简单的渲染循环
while (!glfwWindowShouldClose(window))
{
    /// 处理是否关闭窗口
    processInput(window);

    /// 设置每次清屏的颜色
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    /// glfwSwapBuffers函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲)
    /// 它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上
    glfwSwapBuffers(window);
    
    /// glfwPollEvents函数检查有没有触发什么事件
    ///(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
    glfwPollEvents();
}

// 关闭窗口
glfwTerminate();
return 0;

接下来给出完整代码

完整代码

main.cpp

cpp 复制代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#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()
{
    // 调用 glfwInit(); 以此来初始化我们的窗口 
    glfwInit();

    /// glfwWindowHint 配置我们的GLFW
    /// glfwWindowHint函数的第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择
    /// 第二个参数接受一个整型,用来设置这个选项的值
    /// OpenGL 3.3 因此我们的OpenGL的版本号设置为3.3
    /// 我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)。
    /// 明确告诉GLFW我们需要使用核心模式意味着我们只能使用OpenGL功能的一个子集(没有我们已不再需要的向后兼容特性)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    /// 如果你使用的是MacOS系统那么想要让我们的上面的配置文件生效就需要添加下面的代码
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    /// 创建窗口 GLFWWindow窗口 
    /// 函数需要窗口的宽和高作为它的前两个参数。
    /// 第三个参数表示这个窗口的名称(标题),这里我们使用"LearnOpenGL",当然你也可以使用你喜欢的名称。
    /// 最后两个参数我们暂时忽略。
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "OpenGL Template", NULL, NULL);
    if (window == NULL)
    {
        /// 一旦我们的窗口无法创建,我们就会报错
        std::cerr << "Failed to create GLFW window" << std::endl;
        // 关闭窗口
        glfwTerminate();
        return -1;
    }
    /// 创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了。
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    /// 在之前的教程中已经提到过,GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD
    /// 我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。
    /// GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /// 我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。
    /// 我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。
    /// 因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。
    /// 下面几行的代码就实现了一个简单的渲染循环
    while (!glfwWindowShouldClose(window))
    {
        /// 处理是否关闭窗口
        processInput(window);

        /// 设置每次清屏的颜色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        /// glfwSwapBuffers函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲)
        /// 它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上
        glfwSwapBuffers(window);
        
        /// glfwPollEvents函数检查有没有触发什么事件
        ///(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
        glfwPollEvents();
    }

    // 关闭窗口
    glfwTerminate();
    return 0;
}

/// <summary>
/// 我们同样也希望能够在GLFW中实现一些输入控制,这可以通过使用GLFW的几个输入函数来完成。
/// 我们将会使用GLFW的glfwGetKey函数,它需要一个窗口以及一个按键作为输入。
/// 这个函数将会返回这个按键是否正在被按下。我们将创建一个processInput函数来让所有的输入代码保持整洁
/// </summary>
/// <param name="window"></param>
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

/// <summary>
/// 然而,当用户改变窗口的大小的时候,视口也应该被调整。
/// 我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。这个回调函数的原型如
/// </summary>
/// <param name="window">窗口指针</param>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}
相关推荐
Savior`L1 小时前
二分算法及常见用法
数据结构·c++·算法
摇滚侠2 小时前
ElasticSearch 教程入门到精通,文档创建查询修改删除,笔记10、11、12
笔记·elasticsearch
mmz12072 小时前
前缀和问题(c++)
c++·算法·图论
ULTRA??2 小时前
初学protobuf,C++应用例子(AI辅助)
c++·python
旖旎夜光2 小时前
list实现(7)(上)
c++
大、男人2 小时前
DeepAgent学习
人工智能·学习
不会c嘎嘎2 小时前
深入理解 C++ 异常机制:从原理到工程实践
开发语言·c++
测试人社区—66793 小时前
提升测试覆盖率的有效手段剖析
人工智能·学习·flutter·ui·自动化·测试覆盖率
崇山峻岭之间3 小时前
C++ Prime Plus 学习笔记026
c++·笔记·学习