OpenGL学习笔记【4】——创建窗口,给窗口添加渲染颜色

news2024/12/22 10:40:55

 一、前三章节的前情回顾

章节一:上下文(Context)

   OpenGL学习笔记【1】——简介-CSDN博客

      章节一讲述了OpenGL在渲染的时候需要一个Context来记录了OpenGL渲染需要的所有信息和状态,可以把上下文理解成一个大的结构体,它里面记录了当前绘制使用的颜色、是否有光照计算以及开启的光源等。不同的操作系统,都有各自的上下文创建方法,最简单的上下文可以通过GLFW创建。

章节二:GLFW库

OpenGL学习笔记【2】——开发环境配置(GLFW,VS,Cmake),创建第一个项目-CSDN博客

       章节二讲述了一个专门的窗口库:GLFW库,一个轻量级的图形界面框架,GLFW 的主要功能创建并管理窗口和 OpenGL 上下文,同时还提供了处理手柄、键盘、鼠标输入的功能。

      章节二还创建了一个空项目MyFirstOpenGL。

章节三: GLAD库

  OpenGL学习笔记【3】—— GLAD配置-CSDN博客

     章节三讲述了GLAD库是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD,从而让我们能够使用所有OpenGL函数。

    章节三还创建了一个main.cpp文件,代码中引入了GLAD和GLFW的两个库文件。

前三章节准备工作已经完成,接下来可以开始创建窗口了

二、创建窗口步骤

  2.1、初始化GLFW库

   glfwInit()函数:  一般我们使用Glfw库,首先初始化GLFW库,即需要调用glfwInit()得到OpenGL随显卡驱动一起发布的新特性的函数入口地址。

  2.2、 glfwWindowHint配置GLFW

     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)

       使用核心模式(Core-profile),在调用一个OpenGL的废弃函数时会产生invalid operation 错误,当意外的使用了不该使用的旧函数时是一个很好的提醒。

    注意:请确认您的系统支持OpenGL3.3或更高版本,否则此应用有可能会崩溃或者出现不可预知的错误,如果你的OpenGL版本低于3.3,检查一下显卡是否支持OpenGL 3.3+(不支持的话你的显卡真的太老了),并更新你的驱动程序,有必要的话请更新显卡。

  2.3、创建一个窗口对象

(1) glfwCreateWindow()函数:创建窗口对象 返回值类型GLFWwindow*

参数1,2为窗口的宽度和高度,参数3是窗口的名字,最后两个参数可以忽略。

glfwCreateWindow 创建了一个宽度为800像素、高度为600像素的窗口。

  注意: 其实处理过的的窗口值是从-1到1的显示,而(0,0)点就是窗口的中心。

(2) glfwMakeContextCurrent(window)函数:

将窗口window设置为当前线程的主上下文,即捕获当前窗口,准备对当前窗口进行画图.

 (3) glfwTerminate(); 终止GLFW,释放资源。

2.4、渲染循环

      为了在主动关闭GLFW之前可以不断绘制,在OpenGL中需要添加一个while循环,我们可以把它称之为渲染循环(Render Loop)。如果没有渲染循环,程序运行的话渲染效果只会出现一次就会快速消失。

 glfwWindowShouldClose(window)函数:检查GLFW是否被要求退出。

2.5、点击本地Windows调试器运行测试

运行结果如下就表示窗口创建成功了

2.6、窗口创建成功,接下来给窗口添加渲染颜色

      上面创建的窗口是白色的,我们给这个窗口上个颜色,但是给窗口上颜色之前,需要先初始化GLAD。

      (1)初始化GLAD

        因为用来给窗口上色的相关函数是openGL的函数由第三章节对于GLAD的介绍我们已经知道在调用任何OpenGL的函数之前需要先初始化GLAD,初始化后我们才能够使用所有的OpenGL函数。

//初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    //初始化失败,即加载函数地址失败,打印错误信息
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

    gladLoadGLLoader(GLADloadproc)函数:根据我们编译的系统,GLAD库中用于加载系统相关的OpenGL函数指针地址。参数1为GLADloadproc类型的函数指针地址。这里我们通过GLFW中的glfwGetProcAddress属性获取了函数指针的的地址。

    初始化GLAD后,接下来就能够使用所有OpenGL函数 了,即可以给窗口上色了。

 (2) 给窗口添加渲染颜色(给窗口上色)

        我们要把所有的渲染(Rendering)操作放到渲染循环中,因为我们想让这些渲染指令在每次渲染循环迭代的时候都能被执行。

   既然渲染指令放在渲染循环里,渲染指令又是OpenGL函数,所以说初始化GLAD函数要放在渲染循环代码的前面。

   while (!glfwWindowShouldClose(window))
    {    
        //设置窗口颜色为黑色(参数1,2,3,4对应:R,G,B,A)
         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓冲
          glfwSwapBuffers(window);
    } 

   glClearColor()函数:清空当前窗口的所有颜色,并且给窗口设置成指定参数的背景色;

   glClear(GL_COLOR_BUFFER_BIT)函数:将缓存清除为预先的设置值(设置窗口为黑色);

   glClear传入的参数是:缓冲位(Buffer Bit),这里我们只关心颜色值,所以只清空颜色缓冲即可。

   颜色缓冲(GL_COLOR_BUFFER_BIT)这个缓冲区更新存储颜色数据;

   深度缓冲(GL_DEPTH_BUFFER_BIT):这个缓冲区存储顶点深度值;

  模板缓冲(GL_STENCIL_BUFFER_BIT):用来做模板测试。模板缓冲类似于深度缓冲。模板测试的结果决定了像素的颜色值是否要被写入到渲染目标,像素的深度值是否要被写入深度缓冲。

     glClearColor函数是一个设置函数,而glClear函数是一个使用的函数,glClear使用了当前设置的颜色给窗口上色
 

glfwSwapBuffers(window)函数交换(Swap)前缓冲和后缓冲.

    为什么要交换缓冲?

       因为电脑绘图是一个个像素逐一画的,需要时间,如果单一缓冲,我们可能会看到具体绘画过程,会造成屏幕闪烁等问题,所以为了解决这个问题,这里用了双缓冲技术,用两个内存区域来保存数据,分为前缓冲区和后缓冲区,前缓冲区用于展示屏幕上的内容,而后缓冲区就用来绘制,然后每一帧开始的时候,将两个缓冲区交换,这样后缓冲区又可以画新的内容。

单缓冲和双缓冲的区别?
       单缓冲:是将所有的绘图指令在窗口上执行,就是直接在窗口上绘图,这样的绘图效率是比较慢的,如果使用单缓冲,而电脑比较慢,你回看到屏幕的闪烁。
     双缓冲:实际上的绘图指令是在一个缓冲区完成,这里的绘图非常的快,在绘图指令完成之后,再通过交换指令(glfwSwapBuffers(window))把完成的图形立即显示在屏幕上,这就避免了出现绘图的不完整,同时效率很高。 一般用OpenGL绘图都是用双缓冲,单缓冲一般只用于显示单独的一副非动态的图像。
   点击运行,查看给窗口上色的效果:

  

三、按下ESC键可以关闭GLFW窗口

        通过章节二我们知道GLFW库提供了处理手柄、键盘、鼠标输入的功能,接下来我们就实现按下Esc键关闭我们创建的窗口。

(1)编写processInput函数

  注意:processInput函数一定要放在main函数前面

//监听键盘输入函数
void processInput(GLFWwindow* window) {
    //按下ESC键,关闭指定窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {       
        glfwSetWindowShouldClose(window, true);
    }
}

    glfwGetKey(参数1,参数2):

   参数1:响应窗口对象;参数2:按下的键;返回值:int。
 

    glfwSetWindowShouldClose(参数1,参数2):

   参数1:响应窗口对象,参数2:用来设置是否关闭窗口。

(2)调用processInput函数
     

 glfwPollEvents()函数的作用是处理并接收窗口事件,例如鼠标移动、键盘输入等。通常,该函数需要在每一帧渲染循环中被调用,以便及时地接收并处理用户的输入。

四、释放/删除之前分配的所有资源

  //终止GLFW,释放所有资源====================
    glfwTerminate();

   此章节的代码如下所示:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>// C++的标准输入输出头文件
//监听键盘输入函数
void processInput(GLFWwindow* window) {
    //按下ESC键,关闭指定窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {       
        glfwSetWindowShouldClose(window, true);
    }
}
int main() 
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置主板本号为3
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//设置副版本号为3
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //创建窗口对象 返回值类型:GLFWwindow*
    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)//创建窗口对象失败,打印错误信息
    {    
        std::cout << "Failed to create GLFW window" << std::endl;//需要引用iostream头文件      
        glfwTerminate(); //终止GLFW
        return -1;
    }  
    glfwMakeContextCurrent(window); //将窗口的上下文设置为当前线程的主上下文

    //初始化GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        //初始化失败,即加载函数地址失败,打印错误信息
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    //渲染循环
    while (!glfwWindowShouldClose(window))
    {    
        //设置窗口颜色为黑色(参数1,2,3,4对应:R,G,B,A)
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓冲
        glfwSwapBuffers(window);
        //检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态
        glfwPollEvents();
        //调用按下Esc键,关闭窗口
        processInput(window);      
    }

   //终止GLFW,释放所有资源====================
    glfwTerminate();
 
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1543791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【@changesets/cli】变更集实战教程

一、背景概述 前端目前基于Monorepo架构的npm包开发很普遍&#xff0c;在开发完毕后&#xff0c;我们需要对包进行版本号升级&#xff0c;并且部署&#xff0c;这些操作如果是手动来操作的话&#xff0c;很麻烦&#xff0c;而且容易出错。 例如有这样的场景&#xff1a; -ap…

【可用Claude Opus模型】Claude3国内镜像站,亲测完全超越GPT-4(可用Claude Opus,官网价值20刀)

#今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用Claude 3吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像站到处都是…

C++ 扫描当前路径下文件并删除大文件

C 扫描当前路径下文件并删除大文件 C获取当前路径扫描文件路径下规定后缀名称的文件计算文件大小 1. 获取当前路径 使用<Windows.h>中的GetCurrentDirectory方法实现&#xff0c;单独编写验证程序如下&#xff1a; #include<iostream> #include<Windows.h&g…

YOLOv9改进策略:IoU优化 | Powerful-IoU更好、更快的收敛IoU,效果秒杀CIoU、GIoU等 | 2024年最新IoU

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;Powerful-IoU更好、更快的收敛IoU&#xff0c;是一种结合了目标尺寸自适应惩罚因子和基于锚框质量的梯度调节函数的损失函数 &#x1f4a1;&#x1f4a1;&#x1f4a1;MS COCO和PASCAL VOC数据集实现涨点 YO…

R语言基础入门

1.保存或加载工作空间 改变工作目录——进行文件读写&#xff0c;默认去指定文件进行操作。&#xff08;使用R时&#xff0c;最好先设定工作目录&#xff08;setwd(),getwd()&#xff09;&#xff09; setwd(“工作文件路径”)&#xff1a;建立工作目录 getwd&#xff08;&…

【Mysql数据库基础07】DDL 数据定义语言

Data Definition Language 1 库的操作1.1 create 创建1.2 alter 修改1.3 drop 删除 2 表的操作2.1 表的创建2.2 表的修改2.2.1 修改表名2.2.2 修改列名2.2.3 修改列的类型和约束2.2.4 添加列2.2.5 删除列 2.3 表的删除2.4 表的复制 3 练习 1 库的操作 1.1 create 创建 create…

jvm(虚拟机)运行时数据区域介绍

Java虚拟机&#xff08;JVM&#xff09;运行时数据区域是Java程序在运行过程中使用的内存区域&#xff0c;它主要包括以下几个部分&#xff1a; 程序计数器&#xff08;Program Counter Register&#xff09;&#xff1a; 程序计数器是一块较小的内存区域&#xff0c;是线程私有…

法律合规| AI产品法律风险应对措施全解析

在此前推文中我们全面分析了生成式人工智能算法模型可能遇到的法律风险&#xff0c;那么这些风险应该如何应对呢&#xff1f; 1、隐私泄漏风险&#xff1a;企业需要遵守数据安全法和个人信息保护法的规定&#xff0c;确保数据来源合法&#xff0c;使用时获得用户授权&…

citus的快速开始

准备 dockercitus最新版本&#xff08;docker pull citusdata/citus&#xff09; docker网络 docker network create --subnet172.72.9.0/24 citus-test docker network ls启动citus服务 启动协调节点 docker run -dit --name citus-cod -p 5433:5432 -e POSTGRES_PASSWOR…

【vue3学习之路(一)】

文章目录 前言一、vue3项目创建1.1环境准备1.1.1 基于 vue-cli 创建&#xff08;脚手架创建&#xff09;1.1.2 基于 vite 创建&#xff08;推荐&#xff09; 二、熟悉流程总结 前言 参考视频&#xff1a;https://www.bilibili.com/video/BV1Za4y1r7KE?p10&spm_id_frompag…

使用uniapp 的 plus.sqlite 操作本地数据库报错:::table xxx has no column named xxxx

背景&#xff1a; 1、使用uniapp 的 plus.sqlite 进行APP本地数据库操作 2、SQLite 模块用于操作本地数据库文件&#xff0c;可实现数据库文件的创建&#xff0c;执行SQL语句等功能。 遇到&#xff1a;在之前创建的表上进行新增字段的操作时候&#xff0c;出现问题&#xff1a…

MyEclipse打开文件跳转到notepad打开问题

问题描述 windows系统打开README.md文件&#xff0c;每次都需要右键选择notepad打开&#xff0c;感觉很麻烦&#xff0c;然后就把README.md文件打开方式默认选择了notepad&#xff0c;这样每次双击就能打开&#xff0c;感觉很方便。 然后某天使用MyEclipse时&#xff0c;双击RE…

java第一次作业(二)

先写思路&#xff0c;再写代码&#xff0c;思路清晰&#xff0c;才能写对代码 7-6 求12...n的和 思路&#xff1a; 运用expression的字符串输出 重点&#xff1a; expression输出 代码&#xff1a; import java.util.Scanner; public class Main {public static void main…

数据运营常用的8大模型

✅作者简介&#xff1a;《数据运营&#xff1a;数据分析模型撬动新零售实战》作者、《数据实践之美》作者、数据科技公司创始人、多次参加国家级大数据行业标准研讨及制定、高端企培合作讲师。 &#x1f338;公众号&#xff1a;风姑娘的数字视角&#xff0c;免费分享数据应用相…

为什么静态成员函数不能是虚函数

在面向对象编程中&#xff0c;静态成员函数和虚函数都是常见的概念&#xff0c;但它们之间存在着本质上的差异。由于其特性上的差异&#xff0c;静态成员函数不能声明为虚函数。下面我们来探讨一下为什么静态成员函数不能是虚函数。 我在网上查到最多的说法是静态函数没有this指…

数字化转型能给企业创造哪些价值?

企业数字化转型能创造哪些价值&#xff1f; 深耕TOB行业 9 年&#xff0c;下面来分享下自己的一些经验和看法。 &#xff08;看完要是觉得有用&#xff0c;记得点个赞哈~&#xff09; 1、从宏观上理解&#xff0c;我们可以分成4个大的方面&#xff1a; &#xff08;1&#x…

PTA L2-031 深入虎穴 dfs与bfs版

著名的王牌间谍 007 需要执行一次任务&#xff0c;获取敌方的机密情报。已知情报藏在一个地下迷宫里&#xff0c;迷宫只有一个入口&#xff0c;里面有很多条通路&#xff0c;每条路通向一扇门。每一扇门背后或者是一个房间&#xff0c;或者又有很多条路&#xff0c;同样是每条路…

机器学习-05-回归算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中回归算法&#xff0c;包括线性回归&#xff0c;岭回归&#xff0c;逻辑回归等部分。 参考 fit_transform,fit,transform区别和作用详解&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

结构体讲解

目录 一.结构体类型的声明 (1)结构体的声明 (2)结构体的创建和初始化 (3)匿名结构体 (4)结构体的自引用 二.结构体内存对齐 (1)对齐规则 (2)为什么存在内存对齐&#xff1f; (3)结构体传参 三.结构体实现位段 (1)什么是位段 (2)位段的内存分配 (3)位段的跨平…

GDAL中的地理坐标系、投影坐标系及其相互转换

目录 地理坐标系 国内常用地理坐标系 投影坐标系 国内常用投影坐标系&#xff08;不推荐使用&#xff09; 坐标转换 地理坐标转为投影坐标 投影坐标转为地理坐标 地理坐标系 原理参考这篇文章&#xff1a;地理坐标系与投影坐标系区别与联系 https://yunxingluoyun.blog.…