LearnOpenGl(一)创建窗口

在这里插入图片描述

😎 作者介绍:欢迎来到我的主页👈,我是程序员行者孙,一个热爱分享技术的制能工人计算机本硕,人工制能研究生。公众号:AI Sun(领取大厂面经等资料),欢迎加我的微信交流:sssun902
🎈 本文专栏:本文收录于《LearnOpenGl》系列专栏,相信一份耕耘一份收获,我会分享Opengl相关学习内容,不说废话,祝大家都offer拿到手软
🤓 欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🖥随时欢迎您跟我沟通,一起交流,一起成长、进步!

此系列文章为我记录学习Opengl,原文链接,大模型结合费曼学习,若有博客有不恰当之处,还请包涵~
掌握这些基础知识对于深入学习OpenGL和其他图形API至关重要。

1. 引言

1.1 博客目的

本技术博客旨在为读者提供一个深入浅出的OpenGL入门指南,通过实践示例"Hello, Window"来展示如何创建一个基本的OpenGL窗口并进行渲染。我们的目标是让读者理解OpenGL的基本概念,并能够自行构建简单的图形应用程序。

1.2 预期读者

本博客面向所有对计算机图形学感兴趣的初学者,无需深厚的图形学背景。无论您是学生、业余爱好者还是专业开发者,只要您有基本的编程知识,本博客都将引导您走进OpenGL的世界。

在这里插入图片描述

2. 窗口创建基础

2.1 GLFW库介绍

GLFW是一个开源的、多平台的库,用于创建窗口、接收输入和处理事件。它提供了一个简单易用的API,使得开发者能够快速地创建和管理窗口,而无需关心底层操作系统的细节。

  • 跨平台支持:GLFW支持Windows、macOS和Linux等多个操作系统,为开发者提供了统一的接口来处理不同平台的窗口创建和管理。
  • 易于集成:GLFW与OpenGL、DirectX和Vulkan等图形API紧密集成,方便开发者在不同的渲染上下文中使用。
  • 事件处理:GLFW能够处理键盘、鼠标等输入设备的事件,使得开发者可以轻松地实现用户交互。

2.2 窗口初始化过程

窗口初始化是OpenGL编程的第一步,以下是使用GLFW进行窗口初始化的基本步骤:

  1. 初始化GLFW:首先,需要调用glfwInit函数来初始化GLFW库。这是创建窗口之前的必要步骤。

  2. 设置配置:在创建窗口之前,可以通过glfwWindowHint函数设置窗口的各种属性,如窗口的初始大小、是否可见、OpenGL的版本等。

  3. 创建窗口:使用glfwCreateWindow函数创建窗口。此函数需要窗口的宽度和高度作为参数,并返回一个GLFWwindow对象的指针。

  4. 获取上下文:创建窗口后,需要通过调用glfwMakeContextCurrent函数来获取OpenGL上下文,这样才能在窗口上进行渲染。

  5. 事件循环:在窗口中,需要设置一个事件循环来处理用户的输入和窗口的其他事件。可以通过glfwSetKeyCallback等函数设置回调函数来响应不同的事件。

  6. 渲染循环:在事件循环中,除了处理事件外,还需要进行渲染循环,不断地调用glfwSwapBuffers来交换前后缓冲区,实现图像的更新。

  7. 清理资源:在窗口关闭前,需要调用glfwTerminate来清理GLFW分配的资源,确保程序的稳定退出。

通过以上步骤,可以完成一个基本的窗口创建和管理过程,为OpenGL编程打下坚实的基础。

3. OpenGL环境配置

3.1 版本与核心模式设置

OpenGL(Open Graphics Library)是一个跨语言、跨平台的图形API,广泛用于渲染2D和3D矢量图形。在配置OpenGL环境时,首先需要确定使用的版本。OpenGL有多个版本,包括2.x、3.x、4.x等,每个版本都支持不同的功能集。

  • 版本选择:选择OpenGL的版本取决于目标平台和所需的功能。例如,OpenGL 3.3及以上版本引入了核心模式(Core Profile),它只包含现代OpenGL的特性,不包括旧的遗留特性。
  • 核心模式:核心模式设置是为了确保应用程序使用OpenGL的最新特性,避免使用已废弃的函数,从而提高性能和兼容性。在创建OpenGL上下文时,可以通过设置属性来请求核心模式。

3.2 兼容性与前向兼容

兼容性是OpenGL环境配置中的另一个重要考虑因素。

  • 兼容性概念:OpenGL的兼容性模式(Compatibility Profile)包括了所有旧版本的特性,适合需要在旧硬件上运行的应用程序。
  • 前向兼容:前向兼容(Forward-Compatible)意味着应用程序使用的特性集是面向未来的,不依赖于旧的扩展或特性。这通常与核心模式结合使用,以确保应用程序在新的硬件和驱动上能够运行。
  • 配置示例:在使用现代OpenGL(3.0及以上版本)时,可以通过设置请求核心模式,例如在Windows上使用WGL_CONTEXT_CORE_PROFILE_BIT属性,而在macOS上使用NSOpenGLPFAOpenGLProfile属性。

正确配置OpenGL环境对于开发高性能和可移植的图形应用程序至关重要。通过选择适当的版本和模式,开发者可以确保应用程序利用最新的图形技术,同时保持与未来硬件和操作系统的兼容性。

4. 窗口对象与上下文

4.1 窗口对象的创建与管理

窗口对象是图形用户界面中的基本构成元素,为用户提供了一个交互界面。在OpenGL中,窗口对象的创建和管理通常依赖于第三方库,例如GLFW或SDL。

  • 第三方库的作用:简化窗口和上下文的创建过程,提供跨平台的兼容性。
  • 创建流程:初始化库、设置窗口参数、创建窗口、获取上下文。

4.2 上下文(Context)的概念与重要性

在OpenGL中,上下文定义了渲染状态和GLSL程序的执行环境。每个窗口对象都需要一个上下文来执行渲染命令。

  • 上下文的作用:存储OpenGL状态、纹理、缓冲区等资源信息。
  • 上下文的创建:通过第三方库函数创建,与窗口对象关联。
  • 上下文的激活:在渲染前必须激活相应的上下文,以确保渲染命令的正确执行。

4.3 窗口与上下文的交互

窗口对象和上下文之间的关系密切,窗口提供了渲染的画布,而上下文则定义了在该画布上如何渲染。

  • 渲染流程:设置窗口大小、创建着色器、编写顶点数据、绘制图形。
  • 事件处理:监听用户输入,如键盘和鼠标事件,对渲染过程进行控制。
  • 双缓冲技术:为了避免画面撕裂,大多数窗口系统使用双缓冲技术,即在一个缓冲区进行渲染,同时在另一个缓冲区显示结果。

4.4 窗口的关闭与资源释放

在应用程序结束时,需要正确关闭窗口并释放与窗口相关的资源,以避免内存泄漏和其他资源浪费。

  • 关闭窗口:调用第三方库提供的关闭窗口函数。
  • 资源释放:释放与窗口关联的所有资源,包括纹理、缓冲区等。
  • 库的清理:在所有窗口关闭后,清理并卸载第三方库。

5. GLAD函数指针加载器

GLAD是一个用于加载OpenGL函数指针的库,它允许开发者以一种更为现代和跨平台的方式使用OpenGL。

5.1 GLAD初始化

初始化GLAD是开始使用OpenGL的第一步,以下是初始化GLAD的一般步骤:

  • 安装GLAD:首先,需要在你的项目中包含GLAD库。这可以通过多种方式完成,例如使用包管理器或直接下载源码。

  • 包含GLAD:在你的代码中,需要包含GLAD的头文件,通常如下所示:

    #include <glad/glad.h>
    
  • 创建OpenGL上下文:在初始化GLAD之前,必须创建一个OpenGL上下文。这可以通过多种窗口创建库来完成,比如GLFW或SDL。

  • 调用gladLoadGLLoader:一旦创建了OpenGL上下文,就可以使用GLAD提供的函数来加载所有的OpenGL函数指针。通常如下调用:

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        // 初始化失败的处理
    }
    
  • 验证OpenGL版本:在GLAD成功加载后,可以通过查询OpenGL版本来确认其可用性:

    const GLubyte* renderer = glGetString(GL_RENDERER); // 获取渲染器信息
    const GLubyte* version = glGetString(GL_VERSION); // 获取OpenGL版本
    
  • 使用OpenGL功能:加载完成后,就可以开始使用OpenGL的所有功能了。例如,可以设置视口、清屏颜色等:

    glViewport(0, 0, 800, 600); // 设置视口大小
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清屏颜色为深蓝色
    
  • 渲染循环:在初始化GLAD并设置好OpenGL状态后,可以进入渲染循环,开始绘制图形:

    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT); // 清屏
        // 绘制图形的代码
        glfwSwapBuffers(window); // 交换前后缓冲区
        glfwPollEvents(); // 处理事件
    }
    

通过以上步骤,可以成功地初始化GLAD并开始使用OpenGL进行图形编程。

6. 视口设置与回调函数

在OpenGL中,视口设置和回调函数是渲染流程中的重要组成部分。视口定义了最终图像在窗口中的显示区域,而回调函数则允许开发者处理各种事件,如键盘和鼠标输入。

6.1 视口的概念与设置

视口是OpenGL中的一个抽象概念,它将逻辑坐标映射到窗口的像素坐标。在进行渲染之前,必须正确设置视口,以确保图像能够正确显示。

  • 视口的创建:视口的创建涉及到设置其宽度和高度,这通常与窗口的尺寸相匹配。OpenGL允许开发者通过glViewport函数来定义视口的大小和位置。
  • 视口的作用:视口不仅决定了图像的显示区域,还影响了深度测试和裁剪等操作。因此,正确设置视口对于实现预期的渲染效果至关重要。

6.2 回调函数的作用与实现

回调函数是OpenGL中处理事件的一种机制。通过注册回调函数,开发者可以响应键盘、鼠标等输入事件,从而实现交互式渲染。

  • 键盘回调函数:通过注册键盘回调函数,可以捕获键盘输入,并根据输入执行相应的操作,如移动物体、切换视角等。
  • 鼠标回调函数:鼠标回调函数允许开发者捕获鼠标的移动、点击等事件,并据此调整摄像机位置或触发其他操作。
  • 回调函数的注册:在GLFW中,可以通过glfwSetKeyCallbackglfwSetCursorPosCallback等函数来注册键盘和鼠标的回调函数。

6.3 视口与回调函数的结合使用

在实际应用中,视口设置和回调函数通常结合使用,以实现动态的渲染效果和用户交互。

  • 动态调整视口:在回调函数中,可以根据用户的输入动态调整视口的大小和位置,从而实现不同的视图效果。
  • 事件驱动的渲染:通过回调函数捕获的事件来触发渲染流程,可以实现基于事件的渲染控制,提高渲染的灵活性和响应性。

6.4 实例分析

在OpenGL的学习过程中,通过具体的实例来理解视口设置和回调函数的应用是非常有帮助的。

  • 示例代码:展示如何使用glViewport设置视口,以及如何通过glfwSetKeyCallback注册键盘回调函数。
  • 效果展示:通过运行示例代码,展示视口设置和回调函数在实际渲染中的效果,加深对概念的理解。

通过上述内容的详细阐述,可以为读者提供一个全面而深入的视角,理解视口设置与回调函数在OpenGL渲染流程中的重要性和应用方法。
在这里插入图片描述

7. 渲染循环与双缓冲

渲染循环是OpenGL程序的核心,它是一个持续运行的循环,不断地处理输入、更新场景并渲染图像。双缓冲技术则用于避免图像撕裂和闪烁,提供平滑的视觉体验。

7.1 渲染循环

渲染循环是OpenGL程序的心脏,它负责不断地执行以下步骤:

  • 处理输入事件:如键盘、鼠标等,这些事件可能会影响场景的状态或摄像机的位置。
  • 更新场景:根据物理引擎或用户输入更新对象的位置、旋转等属性。
  • 渲染场景:清除屏幕,设置视图,绘制场景中的所有对象。

渲染循环通常包含以下代码结构:

while (!glfwWindowShouldClose(window)) {
    // 处理输入事件
    processInput(window);

    // 渲染
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    renderScene();
    
    // 交换缓冲区
    glfwSwapBuffers(window);

    // 轮询和处理事件
    glfwPollEvents();
}

7.2 双缓冲

双缓冲是一种用于减少图像撕裂和闪烁的技术。它涉及两个缓冲区:前缓冲区和后缓冲区。前缓冲区是当前显示在屏幕上的图像,而后缓冲区是下一个要渲染的图像。

  • 前缓冲区:当前显示的图像。
  • 后缓冲区:下一个要渲染的图像。

在渲染循环中,我们首先在后缓冲区中渲染图像,然后交换两个缓冲区,使得后缓冲区的内容显示在屏幕上。这个过程可以减少图像在渲染过程中被部分更新的情况,从而避免撕裂和闪烁。

双缓冲的实现

在OpenGL中,双缓冲通常是通过调用glClear(GL_COLOR_BUFFER_BIT)来实现的,它会清除当前绑定的帧缓冲区的内容。然后,我们绘制场景,最后通过glfwSwapBuffers(window)交换缓冲区。

glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区
renderScene(); // 渲染场景
glfwSwapBuffers(window); // 交换缓冲区

双缓冲的优势

双缓冲的主要优势在于:

  • 减少撕裂:通过在后缓冲区渲染,我们可以在不影响当前显示图像的情况下更新图像。
  • 减少闪烁:避免了在渲染过程中部分更新图像,从而减少了闪烁。
  • 提高视觉体验:提供平滑、连贯的图像更新,增强了用户体验。

通过以上步骤,我们可以确保OpenGL程序能够以稳定和高效的方式渲染图像,同时提供高质量的视觉体验。

8. 输入处理与事件

在图形编程中,输入处理和事件管理是实现交互性的关键组成部分。以下是对这一主题的深入探讨。

8.1 输入设备与数据获取

输入设备,如键盘、鼠标、触摸板等,提供了用户与应用程序之间的交互手段。每种输入设备都能够生成特定的输入事件,例如键盘的按键事件、鼠标的点击或移动事件。

  • 键盘输入:可以通过监听特定的按键状态(按下、释放)来实现文本输入或快捷键功能。
  • 鼠标输入:包括位置信息、滚轮滚动以及各个按钮的点击状态,常用于3D视图中的相机控制和对象选择。

8.2 事件处理机制

事件处理机制允许应用程序响应用户的操作。在OpenGL中,事件通常由窗口系统管理,并传递给应用程序。

  • 事件队列:操作系统通常会维护一个事件队列,应用程序需要定期检查并处理队列中的事件。
  • 事件分发:应用程序接收到事件后,根据事件类型和内容将其分发给相应的处理函数。

8.3 事件驱动的编程模型

事件驱动的编程模型是一种异步处理方式,它允许应用程序在不阻塞主线程的情况下响应用户输入。

  • 回调函数:为不同的事件类型定义回调函数,当事件发生时,相应的回调函数被调用。
  • 事件监听器:在对象中注册事件监听器,当事件发生时,监听器会被通知并执行相应的操作。

8.4 处理常见输入事件

理解和处理常见的输入事件是创建交互式应用程序的基础。

  • 按键事件:处理键盘输入,实现如移动、跳跃等控制功能。
  • 鼠标事件:处理鼠标的位置变化、点击和释放,实现视角的旋转和平移。
  • 触摸事件:对于触摸屏设备,处理多点触控事件,实现缩放、旋转等手势操作。

8.5 集成第三方库

在OpenGL开发中,经常使用第三方库来简化输入处理和事件管理。

  • GLFW:一个开源的库,提供了创建窗口、上下文管理和事件处理的功能。
  • SDL:另一个流行的库,除了窗口和事件管理,还提供了音频处理和文件I/O等功能。

8.6 性能考虑

输入处理和事件响应的速度直接影响用户体验。

  • 事件处理的优化:确保事件处理逻辑尽可能高效,避免在主线程中执行耗时操作。
  • 异步事件处理:考虑使用异步事件处理机制,减少对主渲染循环的影响。

8.7 安全性与异常处理

处理用户输入时,需要考虑到安全性和异常情况。

  • 输入验证:对所有用户输入进行验证,防止恶意数据导致程序崩溃或安全漏洞。
  • 异常捕获:妥善处理事件处理过程中可能出现的异常,确保程序的稳定性。

通过上述分析,我们可以看到输入处理与事件管理在图形编程中的重要性,以及实现高效、稳定和安全的用户交互所需的关键技术和策略。

9. 渲染操作与清屏

渲染操作是图形编程的核心部分,它负责将我们创建的图像或3D模型显示在屏幕上。在OpenGL中,这个过程涉及到几个关键步骤,包括设置视口、清屏、以及调用绘图命令。

9.1 设置视口

视口(Viewport)定义了渲染图像在窗口中的显示区域。在OpenGL中,视口是一个矩形区域,其大小和位置可以通过glViewport函数设置。通常,视口的大小与窗口的大小相匹配,但也可以调整以实现特定的渲染效果。

9.2 清屏操作

在每次渲染循环之前,需要清除屏幕上的图像,以便为新的渲染内容腾出空间。OpenGL提供了几种清屏选项,包括清除颜色缓冲区、深度缓冲区和模板缓冲区。使用glClear函数可以一次性清除多个缓冲区。

9.3 绘制调用

绘制调用是OpenGL中实际执行渲染操作的命令。OpenGL提供了多种绘制模式,如点、线、三角形等。根据需要绘制的几何体类型,可以使用glDrawArraysglDrawElements函数来执行绘制操作。

9.4 交换缓冲区

在双缓冲模式下,OpenGL使用两个缓冲区:前缓冲区和后缓冲区。渲染操作首先在后缓冲区进行,完成后通过交换缓冲区的操作将后缓冲区的内容显示在屏幕上。这个过程可以通过调用glutSwapBuffers(在使用GLUT的情况下)或其他交换缓冲区的函数来实现。

9.5 渲染循环

渲染循环是图形应用程序的心脏,它不断重复执行渲染操作,以更新屏幕上的图像。在每次循环中,通常包括处理输入事件、更新场景状态、执行渲染操作、清屏和交换缓冲区等步骤。

效果图

在这里插入图片描述
我的源码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

void proessInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, true);
    }
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    GLFWwindow* window = glfwCreateWindow(800, 600, "Worker Duan", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW Window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "failed to initialized GLAD" << std::endl;
        return -1;
    }

    glViewport(0, 0, 800, 600);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);   
    //while (!glfwWindowShouldClose(window))
    //{
    //    glfwSwapBuffers(window);
    //    glfwPollEvents();
    //}
    while (!glfwWindowShouldClose(window))
    {
        proessInput(window);
        glClearColor(0.2f, 0.7f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
    return 0;

}

祝大家学习顺利~
如有任何错误,恳请批评指正~~
以上是我通过各种方式得出的经验和方法,欢迎大家评论区留言讨论呀,如果文章对你们产生了帮助,也欢迎点赞收藏,我会继续努力分享更多干货~


🎈关注我的公众号AI Sun可以获取Chatgpt最新发展报告以及腾讯字节等众多大厂面经
😎也欢迎大家和我交流,相互学习,提升技术,风里雨里,我在等你~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员行者孙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值