OpenGL 入门(一)— 创建窗口

文章目录

  • 前言
  • 创建一个窗口
    • 视口动态调整
    • 输入控制
    • 渲染
  • 完整代码

前言

关键词介绍:

  • OpenGL: 一个定义了函数布局和输出的图形API的正式规范。
  • GLFW:一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文、定义窗口参数以及处理用户输入,对我们来说这就够了。
  • GLAD: 一个扩展加载库,用来为我们加载并设定所有OpenGL函数指针,从而让我们能够使用所有(现代)OpenGL函数。
  • 视口(Viewport): 我们需要渲染的窗口。

创建一个窗口

首先,引入头文件

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

接下来,实例化GLFW窗口:

    //初始化GLFWglfwInit();//配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;//第二个参数接受一个整型,用来设置这个选项的值。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);

注意:glfwWindowHint函数的第一个参数代表选项的名称,很多以GLFW_开头的枚举值中选择,该函数的所有的选项以及对应的值都可以在 GLFW’s window handling 这篇文档中找到,具体怎么用可以查看文档。

然后,我们创建一个窗口对象:

    //创建一个窗口对象GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if(window == NULL){std::cout << "Failed to create GLFW window" << std::endl;//创建失败,终止程序glfwTerminate();return -1;}//将我们窗口的上下文设置为当前线程的主上下文glfwMakeContextCurrent(window);

这时候编译运行会闪烁一下,说明我们创建成功了,如果保持常显示,需要下一步操作:

    // 循环渲染while (!glfwWindowShouldClose(window)){// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}// 释放/删除之前的分配的所有资源glfwTerminate();
  • glfwWindowShouldClose函数:在我们每次循环的开始前检查一次GLFW是否被要求退出,如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。
  • glfwPollEvents函数:检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
  • glfwSwapBuffers函数:会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
  • glfwTerminate函数:当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源。

到这里编译运行,就会出现窗口了。
完整代码:

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>int main()
{// 初始化GLFWglfwInit();// 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;// 第二个参数接受一个整型,用来设置这个选项的值。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, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;// 创建失败,终止程序glfwTerminate();return -1;}// 将我们窗口的上下文设置为当前线程的主上下文glfwMakeContextCurrent(window);// 初始化GLAD,传入加载系统相关opengl函数指针的函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;// 初始化失败,终止程序return -1;}// 循环渲染while (!glfwWindowShouldClose(window)){// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}// 释放/删除之前的分配的所有资源glfwTerminate();std::cout << "Hello, World!" << std::endl;return 0;
}

在这里插入图片描述

当然,我们也可以参考官方完整代码做些功能扩展:

视口动态调整

在我们开始渲染之前还有一件重要的事情要做,我们必须告诉OpenGL渲染窗口的尺寸大小,即视口(Viewport),这样OpenGL才只能知道怎样根据窗口大小显示数据和坐标。我们可以通过调用glViewport函数来设置窗口的维度

glViewport(0, 0, 800, 600);

glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。

当用户改变窗口的大小的时候,视口也应该被调整。我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。

...
// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);// settings 窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;int main()
{...// 设置窗口大小改变时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);...return 0;
}// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{// 设置窗口的维度glViewport(0, 0, width, height);
}

输入控制

使用GLFW的glfwGetKey函数,它需要一个窗口以及一个按键作为输入。这个函数将会返回这个按键是否正在被按下。我们将创建一个processInput函数来让所有的输入代码保持整洁。

// 窗口大小改变时调用...void processInput(GLFWwindow *window);int main()
{...// 循环渲染while (!glfwWindowShouldClose(window)){// 输入processInput(window);// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}...return 0;
}// 输入
void processInput(GLFWwindow *window)
{// 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true// 关闭应用程序if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}

渲染

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

// 渲染循环
while(!glfwWindowShouldClose(window))
{// 输入processInput(window);// 渲染指令glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 检查并调用事件,交换缓冲glfwPollEvents();glfwSwapBuffers(window);
}

完整代码

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);// settings 窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;int main()
{// 初始化GLFWglfwInit();// 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;// 第二个参数接受一个整型,用来设置这个选项的值。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, "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);// 检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}// 释放/删除之前的分配的所有资源glfwTerminate();std::cout << "Hello, World!" << std::endl;return 0;
}// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{// 设置窗口的维度glViewport(0, 0, width, height);
}// 输入
void processInput(GLFWwindow *window)
{// 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true// 关闭应用程序if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}

编译之后,效果展示
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#使用RabbitMQ-2_详解工作队列模式

简介 &#x1f340;RabbitMQ中的工作队列模式是指将任务分配给多个消费者并行处理。在工作队列模式中&#xff0c;生产者将任务发送到RabbitMQ交换器&#xff0c;然后交换器将任务路由到一个或多个队列。消费者从队列中获取任务并进行处理。处理完成后&#xff0c;消费者可以向…

C#,打印漂亮杨辉三角形(帕斯卡三角形)的源代码

杨辉 Blaise Pascal 这是某些程序员看完会哭的代码。 杨辉三角形&#xff08;Yanghui Triangle&#xff09;&#xff0c;是一种序列数值的三角形几何排列&#xff0c;最早出现于南宋数学家杨辉1261年所著的《详解九章算法》一书。 欧洲学者&#xff0c;最先由帕斯卡&#x…

VsCode提高生产力的插件推荐-持续更新中

别名路径跳转 自定义配置// 文件名别名跳转 "alias-skip.mappings": { "~/": "/src", "views": "/src/views", "assets": "/src/assets", "network": "/src/network", "comm…

Go语言指针变量

1. 指针变量 区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 Go语言中的指针3个概念&#xff1a;指针地址、指针类型和指针取值。 1.1. Go语言中的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想要修改某个变量的时候&a…

仓储管理系统——软件工程报告(总体设计)③

总体设计 一、需求规定 软件工程仓库存储管理系统的需求规定是确保系统能够满足用户期望、提高工作效率、确保数据安全性和系统可维护性的基石。其涵盖了功能性、性能、数据管理、用户界面和系统可维护性等多个方面。通过严格的验收标准&#xff0c;可以确保系统在实际应用中…

单片机7-10

目录 定时器 按键控制LED流水灯关键代码 定时器时钟关键代码 串口通信 ​编辑串口向电脑发送数据 电脑通过串口控制LED LED点阵屏 LED点阵屏显示图形 LED点阵屏显示动画 DS1302 DS1302时钟显示 DS1302时钟调节 定时器 为什么12MHz的工作周期为一微秒&#xff1a; 12…

「阿里云」幻兽帕鲁个人服务器已上线,3分钟快速搭建

基于阿里云搭建幻兽帕鲁服务器方法&#xff0c;1到2分钟部署完成&#xff0c;稳定运行无卡顿&#xff0c;阿里云服务器网aliyunfuwuqi.com分享保姆级手把手教程&#xff0c;基于阿里云计算巢、云服务器或无影云桌面都可以&#xff1a; 基于阿里云幻兽帕鲁服务器创建教程 基于…

svn和git的本质区别是什么

参考&#xff1a; https://blog.csdn.net/feiying0canglang/article/details/126550676 上边图中&#xff0c;跨越了区的箭头&#xff0c;它中间的区数据都会同步。例如&#xff1a;git checkout &#xff0c;它是将本地仓库数据更新到暂存区和工作区的。\ 理解 gitlab和svn都…

网工内推 | 网络安全工程师专场,CISP、HCIE安全认证优先

01 八马茶业 招聘岗位&#xff1a;网络安全工程师 职责描述&#xff1a; 1、负责公司信息安全项目实施及技术支持工作&#xff0c;主要包含信息安全风险检查评估、网络安全设备、服务器及网站日志审查等&#xff1b; 2、应用、服务器和网络架构进行安全检查测试&#xff0c;找…

【MySQL源码】Seconds_Behind_Master是如何计算的

作为MySQL DBA&#xff0c;相信大家对参数 Seconds_Behind_Master 并不陌生&#xff0c;该字段的值可以通过 show slave status\G的输出&#xff0c;表示主从延迟的时间&#xff0c;单位为秒。监控主从延迟一般取这个值就足够了。0 表示无延迟&#xff0c;理想状态该值不要超…

【数据分享】1992-2019年中国水土保持防治能力数据集

土壤数据是在环境、农业、生态等相关研究中都非常常用的数据&#xff01;我们之前发表过一篇介绍土壤数据来源的文章&#xff08;可查看之前发布的文章获悉详情&#xff09;&#xff01; 水土保持防治能力是土壤的重要属性。本次我们给大家带来的是1992-2019年中国水土保持防治…

vue3 中组合键 command+Enter / shift+Enter / alt + Enter 实现换行,详细实现

vue3 中组合键实现换行 需求背景 有一个聊天室功能&#xff0c;采用输入框的形式&#xff0c;输入完毕使用Enter&#xff0c;可以直接进行发送。使用一些组合键 比如 commandEnter / shiftEnter / alt Enter … 可以实现换行操作。但现实的情况是&#xff0c;原生 Enter 天然…

uniapp安卓android离线打包本地打包整理

离线打包准备 下载Android studio 1.准备资源hbuilder 2.准备离线SDK 最新android平台SDK下载最新android平台SDK下载 3.离线打包key申请 4.直接导入HBuilder-Integrate-AS工程,直接运行simpleDemo项目即可 5.安装java 1.8 jdk-8u151-windows-x64 6.遇到这个报错报错Caus…

xshell无法连接linux,查询本机ip时出现<NO-CARRIER,BROADCAST,MULTICAST,UP>

在用xshell连接虚拟机VMware中的linux时&#xff0c;发现昨天还能连通的&#xff0c;今天连接不了了 我寻思应该是网卡配置出问题了&#xff0c;就去终端ip addr试了一下&#xff0c;果然发现问题&#xff0c;ip 查看网卡ens33就发现出现ens33:<NO-CARRIER,BROADCAST,MULTI…

SpringSecurity认证登录成功后获取角色菜单

目录 前言 一、RBAC模型 二、实战应用 1. 建立用户、角色、资源实体类 2. 数据层查询角色资源 3. 业务层实现&#xff0c;调用数据层查询接口 4. SystemController控制器菜单获取方法 5. menu.jsp菜单页面实现 前言 本篇文章接SSM项目集成Spring Security 4.X版本&…

解决TortoiseGit软件Git Show log时显示Too many files to display的问题

1 问题描述 有时代码提交修改的文件比较多&#xff0c;当查看log时无法显示出来修改的文件列表&#xff0c;如下所示&#xff1a; 2 解决方法 将LogTooManyItemsThreshold尽可能配置得大一些。 三 参考资料 https://gitlab.com/tortoisegit/tortoisegit/-/issues/3878

常用通信总线学习——RS232与RS485

RS232概述 RS-232标准接口&#xff08;又称EIA RS-232&#xff09;是常用的串行通信接口标准之一&#xff0c;它是由美国电子工业协会(Electronic Industry Association&#xff0c;EIA)联合贝尔系统公司、调制解调器厂家及计算机终端生产厂家于1970年共同制定&#xff0c;其全…

C++核心编程:C++ 中的引用 笔记

2.引用 2.1 引用的基本使用 - 作用&#xff1a;给变量起别名 - 语法&#xff1a;数据类型 &别名 原名 #include<iostream> using namespace std; int main() {// 引用基本语法// 数据类型 &别名 原名int a 10;// 创建引用int &ref_a a;cout<<&qu…

快速入门Playwright框架:从零到自动化测试的第一步

Playwright框架&#xff1a; 背景介绍&#xff1a; ​ Playwright 是微软开发的 Web应用 的 自动化测试框架 。selenium相对于Playwright慢很多&#xff0c;因为Playwright是异步实现的&#xff0c;但是selenium是同步的&#xff0c;就是后一个操作必须等待前一个操作。 sel…

代码随想录 Leetcode226.翻转二叉树

题目&#xff1a; 代码(首刷看解析 2024年1月25日&#xff09;&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {if(root nullptr) return root;swap(root->left,root->right);invertTree(root->left);invertTree(root->right);retu…