首页 > 其他分享 >01 OpenGL初步认知与窗口创建

01 OpenGL初步认知与窗口创建

时间:2023-04-19 19:34:21浏览次数:52  
标签:01 窗口 函数 OpenGL 认知 GL GLFW 上下文

1. 初识OpenGL

本文是之前学习OpenGL所做的笔记,现在转到这里。

1.1 OpenGL(Open Graphics Library)

一般它被认为是一个应用程序编程接口(API),它包含了一系列可以操作图形、图像的方法。

然而,OpenGL本身并不是一个API,仅仅是一个规范,由Khronos组织制定并维护,所有版本的OpenGL规范文档都被公开的寄存在Khronos。

OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。

OpenGL规范并没有规定实现的细节,具体的OpenGL库允许使用不同的实现,只要其功能和结果与规范相匹配。

1.2 核心模式(Core-profile)与立即渲染模式(Immediate mode)

立即渲染模式(固定渲染管线):该模式下绘制图形很方便,大多数功能都被库隐藏起来,容易使用和理解。但灵活性差,开发者很少能控制OpenGL如何进行计算。

核心模式:要求使用者真正理解OpenGL和图形编程,它有一些难度,然而提供了灵活性和高效率,更重要的是可以更深入的理解图形编程。

注意:当使用新版本的OpenGL特性时,只有新一代的显卡能够支持你的应用程序。这也是为什么大多数开发者基于较低版本的OpenGL编写程序,并有选择的启用新特性。

1.3 扩展(Extension)

当一个显卡公司提出一个新特性或者渲染上的大优化,通常会以扩展的方式在驱动中实现。当这个扩展十分流行或非常有用的时候,它最终将成为未来OpenGL规范的一部分。

使用扩展的代码大多如下:

If (GL_ARB_extension_name)
{
//使用硬件支持的全新的现代特性
}
Else
{
//不支持此扩展:用旧的方式去做
}

1.4 状态机(State Machine)

OpenGL自身是一个巨大的状态机:一个描述OpenGL该如何操作的所有变量的大集合。

OpenGL的状态通常被称为OpenGL上下文(Context)。

我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。

我们可以通过改变一些上下文变量来改变OpenGl状态,从而告诉OpenGL如何去绘图。其中状态设置函数(Sate-changing Function)会改变上下文,状态使用函数(State-using Function)则会根据当前的状态执行一系列操作。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。

把OpenGL当作一个大状态机,就能更容易理解它的大部分特性。

补漏:什么是状态机?

基本定义:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

四大概念:

状态(State):一个状态机至少要包含两个状态。例如一个自动门,可以开,关。它有open和closed两个状态。

事件(Event):事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。

动作(Action):事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个Action一般就对应一个函数。

变换(Transition):也就是从一个状态变化为另一个状态。例如“开门过程”就是一个变换。

1.5 对象(Object)

OpenGL库内核是一个C库,由于一些C语言结构不易被翻译到其它高层语言,因此OpenGL设计的时候引入了“对象”这一抽象概念。

在OpenGL中一个对象是指一些选项的集合,代表OpenGL状态的一个子集。

比如,我们可以用一个对象来代表绘图窗口的设置,可以设置它的大小,支持的颜色位数等等。可以把对象看作一个C风格的结构体:

Struct object_name{
GLfloat option1;
Gluint option2;
Glchar[] name;
}

OpenGL定义的这些GL原始类型是平台无关的内存排列方式,而int等类型在不同平台上可能有不同的内存排列方式。使用GL原始类型可以保证你的程序在不同的平台上工作一致。

使用一个对象时,通常看起来如下(把OpenGL上下文比作一个大的结构体):

OpenGL常见工作流

//OpenGL的状态
Struct OpenGL_Context
{
...
Object* object_Window_Target;
...
};

//创建对象
GLuint objectId = 0;
glGenObject(1, &objectId);
//绑定对象至上下文
glBindObject(GL_WINDOW_TARGET, objectId);
//设置GL_WINDOW_TARGET对象的一些选项
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
//将上下文的GL_WINDOW_TARGET对象设回默认
glBindObject(GL_WINDOW_TARGET, 0);

我们首先创建一个对象,然后用一个id保存它的引用(实际数据被储存在后台)。

然后我们将对象绑定至上下文的目标位置(例子里窗口对象的目标位置被定义成GL_WINDOW_TARGET)。

接下来我们设置窗口的选项。

最后我们通过将目标位置的对象id设回0的方式解绑这个对象。

设置的选项被保存在objectID代表的对象中,一旦我们重新绑定这个对象到GL_WINDOW_TARGET位置,这些选项就会重新生效。

使用对象的一个好处是在程序中,我们不只可以定义一个对象,并设置它们的选项,每个对象都可以是不同的设置。

2. GLFW基础知识及窗口创建

一些针对OpenGL的库可以节约书写操作系统相关代码的时间,提供给我们一个窗口和一个OpenGL上下文用来渲染。教程中所用的库为GLFW,它提供了一些渲染物体所需的最低限度的接口。按流程配置好GLFW和GLAD库之后,我们就可以利用这些库来设置OpenGL上下文,创建窗口了。

在创建窗口之前,可以先了解一下GLFW的窗口相关功能。

2.1 窗口对象

GLFWwindow对象封装了窗口和上下文。它们是用glfwCreatewindow创建的,用glfwDestroyWindow或者glfwTerminate销毁。由于窗口和上下文是不可分割的联系在一起的,所以对象指针同时被作用于上下文和窗口句柄。

2.1.2 窗口创建

窗口及其OpenGL或OpenGL ES上下文是用glfwCreateWindow函数创建的,它返回创建的窗口对象的句柄。例如,创建一个窗口模式(windowed mode)下640*480的窗口,执行以下代码:

GLFWwindow* window = glfwCreateWindow(640, 480,  “My Title”, NULL, NuLL);

如果窗口创建失败,将返回NULL,因此需要检查返回值。

全屏窗口覆盖显示器的整个显示区域,没有边框或装饰。要创建全屏窗口(Full screen windows),执行以下代码:

GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", glfwGetPrimaryMonitor(), NULL);

使用glfwSetWindowMonitor函数设置显示器可以使窗口模式变为全屏显示,取消设置的话可以使全屏显示转换为窗口显示。

2.1.3 窗口销毁

当不再需要一个窗口时,用glfwDestroyWindow销毁该窗口。

glfwDestroyWindow(window);

在销毁窗口之前,所有的回调函数都被移除,因此不会再为该窗口传递任何事件。当调用glfwTerminate时,所有剩余的窗口也被销毁。

2.1.4 窗口创建提示

创建窗口和上下文之前,可以设置许多提示。有些影响窗口本身,有些影响帧缓冲区或上下文。:

glfwInit():初始化GLFW库。

glfwWindowHint():单独设置整数值提示。

glfwWindowHintString():单独设置字符串值提示。

glfwDefaultWindowHints():一次将所有提示重置为默认值。

这些提示分为窗口相关提示(Window related hints),帧缓冲区相关提示(Framebuffer related hints),显示器相关提示(Framebuffer related hints),以及上下文相关提示(Context related hints)。

某些提示是特定于某一平台的,其他平台会忽略他们,设置这些提示不需要特定于平台的头或调用。

2.2 实例化GLFW窗口

了解了一些基本的GLFW的窗口相关功能之后,就可以开始创建GLFW窗口了。

#include <glad/glad.h>//GLAD头文件包含了OpenGL头文件,所以在使用其它依赖于OpenGL的头文件之前要先包含GLAD
#include <GLFW/glfw3.h>
int main()
{
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号,当API以不兼容的方式更改时,该值会增加。
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号,当特性被添加到API中时,它会增加,但是它保持向后兼容。
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//使用核心模式
  
    return 0;
}

2.2.1 glfwInit()与glfwTerminate()

glfwInit():

在使用大多数GLFW函数之前,必须初始化GLFW。并且在应用程序终止之前,也应该终止GLFW,以便释放在初始化期间或之后分配的任何资源。如果这个函数调用失败,它在返回之前调用glfwTerminate。如果这个函数调用成功,您应该在应用程序退出之前调用glfwTerminate。

glfwTerminate():

此函数销毁所有剩余的窗口和光标,恢复任何修改的gamma ramps,并释放任何其他分配的资源。调用此函数后,必须再次成功调用glfwInit,才能使用大多数GLFW函数。

2.2.2 glfwCreateWindow函数

该函数创建一个窗口及其相关的OpenGL或OpenGL ES上下文,完整函数名如下:

GLFWwindow * glfwCreateWindow	(int 	width, //以屏幕坐标表示的窗口的期望宽度,必须大于零。
                                 int 	height, //以屏幕坐标表示的窗口期望高度,必须大于零。  
                                 const char * 	title,//窗口标题。
                                 GLFWmonitor * 	monitor,//用于全屏模式的monitor,对于窗口模式为空。
                                 GLFWwindow * 	share //要与其共享资源的上下文的窗口,值为NULL表示不共享资源。
                                 )	

了解了上面几个基础函数之后,我们开始创建一个窗口对象,这个窗口对象存放了所有和窗口相关的数据。

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);

2.2.3 初始化GLAD

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

//给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

2.2.4 framebuffer_size_callback函数

设置视口,确定OpenGL渲染窗口的尺寸大小,这样OpenGL才只能知道怎样根据窗口大小显示数据和坐标。我们可以通过调用glViewport函数来设置窗口的维度:

glViewport(0, 0, 800, 600);//前两个参数控制窗口左下角的位置,第三个和第四个参数控制渲染窗口的宽度和高度。

设置一个帧缓冲大小函数,当用户改变窗口的大小的时候,视口也应该被调整:

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

这是一个回调函数,它会在每次窗口大小被调整的时候被调用,我们会在创建窗口之后,渲染循环初始化之前注册这些回调函数。

使用glfwGetKey函数实现输入控制,它需要一个窗口以及一个按键作为输入,这个函数将会返回这个按键是否正在被按下。

voidprocessInput(GLFWwindow *window){    
 if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)        
 glfwSetWindowShouldClose(window, true); //按下ESC,关闭GLFW
}

2.2.5 渲染循环

保证主动关闭应用程序之前,它能够不断绘制图像并能够接受用户输入。

为测试起见,教程设置了一个自定义的颜色清空屏幕。在每个新的渲染迭代开始的时候清屏。

glClearColor函数是一个状态设置函数,设置颜色和Alpha值,用于清除颜色缓冲区,需要使用的填充值范围被归一化在(0,1)之间。

而glClear函数则是一个状态使用的函数,利用当前缓冲区清除值(glClearColor、glClearDepth、glClearIndex、glClearStencil、glClearAccum)等函数所指定的值来清除指定的缓冲区。

当调用glClear函数,清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。

  while (!glfwWindowShouldClose(window))//glfwWindowShouldClose函数在每次循环开始前检查一次GLFW是否被要求退出,如果是的话该函数返回true,渲染循环结束。
    {
        processInput(window);//循环每次迭代时调用该函数,检测用户是否输入ESC键
        //渲染指令
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//清除颜色设置为X色
        glClear(GL_COLOR_BUFFER_BIT);//执行清除
        //检查并调用事件,交换缓冲
        glfwSwapBuffers(window);//glfwSwapBuffers函数会交换颜色缓冲(储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
        glfwPollEvents();//glfwPollEvents函数检查是否触发事件(比如键盘输入、鼠标移动)、更新窗口状态,并调用对应地回调函数(可以通过回调方法手动设置)
    }

运行代码之后,屏幕颜色如下:

img

参考资料:

GLFW: Window guide

LearnOpenGL CN

标签:01,窗口,函数,OpenGL,认知,GL,GLFW,上下文
From: https://www.cnblogs.com/nexus-helix/p/17334382.html

相关文章

  • 2012 年最佳 Android 应用
    2012年最佳Android应用  •Evernote:云笔记应用•Zappos:美国鞋类电商Zappos官方应用•Pinterest:图片社交网络•Grimm’sSnowWhite:游戏《格林童话:白雪公主》•Expedia:在线旅游网站•Pocket:“稍后再读”应用•Ancestry:家谱网站Ancestry官方应用•Fancy:创意收集应用•......
  • 【MathType】word2016数学公式编号
    问题毕业论文排版中,对数学公式需要类似(3-1)的格式。解决技巧在写论文初稿的时候,先不要于公式的编号,先给它编一个号,比如(3)(2)(4)的。最后写完了以后,再再添加section,同意修改编号格式为(章-公式序号)可以先修改mathtype章节的样式为不隐藏,这样方便添加。添加完math......
  • 数据结构与算法学习01
    学习要点一、预习页码:2-27二、预习内容1、数据结构基本定义:数据、数据元素、数据项、数据对象,重点理解数据结构的定义。2、理解逻辑结构。集合、线性结构、树形结构、图形结构。数学描述方法。3、理解存储结构。顺序存储结构、链式存储结构、索引存储结构和哈希存储结构。4......
  • NOIP 2010 题解
    机器翻译单向链表,如果\(i\)在内存里,那么用\(nxt[i]\)来记录他的下一个单词,每次要插入的时候,如果当前链表的长度小于\(m\),那么直接把他插入的末尾,如果等于\(m\),就把链表的第一个从链表里弹出来,再把这个元素加进去。\(Code:\)#include<bits/stdc++.h>usingnamespacest......
  • 20201306 Exp5 信息搜集与漏洞扫描
    目录一、实践目标及实践内容实践目标实践内容二、实践原理三、实践过程记录1、各种搜索技巧的应用搜索网址目录结构利用搜索引擎搜索特定类型的文件使用traceroute命令进行路由侦查2、DNSIP注册信息的查询whois查询nslookup查询dig查询LP2Location地理位置查询IP......
  • 网络对抗实验五 信息搜集与漏洞扫描--20201313
    目录网络对抗实验五信息搜集与漏洞扫描一、实践目标及实践内容1.实践目标2.实践内容二、实践原理1.信息搜集三、实践过程记录1、各种搜索技巧的应用搜索网址目录结构使用traceroute命令进行路由侦查2、DNSIP注册信息的查询whois查询nslookup查询dig查询LP2Location地理位置查......
  • 280049C_001
    280049C入门入门帮助文档\ti\c2000\C2000Ware_版本\device_support\f28004x\docs目录下的《F28004x_FRM_EX_UG》寄存器开发和库函数开发寄存器开发(bit-field)例程:∼/device_support/f28004x/examplesdirectory库函数开发(driver_library)例程:∼/driverlib/f28004......
  • 19c环境,运行DBCA创建CDB时,报错ORA-01519: error while processing file:?/rdbms/admin
    1、同事新搭建的一套19CRAC,补丁为19.10,运行DBCA安装CDB数据库时报错,错误日志如下所示:ORA-01519:errorwhileprocessingfile:?/rdbms/admin/dcore.bsq.....ORA-00604:erroroccurredatrecursiveSQLlevel1ORA-01119:errorincreatingdatabasefile'+DATA01/CDB1/pdb......
  • P2680 NOIP2015 提高组 运输计划
    P2680NOIP2015提高组运输计划最小化最长的路径,考虑二分答案。问题转化成检验删去一条边的边权后,最长路径权值能否不超过\(x\)。考虑没删边权时,原先那些不超过\(x\)的路径,删去边权后肯定不会影响,直接忽略。考虑原先比\(x\)长的那些路径。我们期望删边权后这些路径全部......
  • 01计算机网络概论
    导图总结1.计算机网络的发展主要经历了4个阶段第一阶段为面向终端的计算机网络,第二阶段为多计算机互联的计算机网络,第三阶段为面向标准化的计算机网络,第四阶段为全球互联的计算机网络。2.计算机网络可定义为把分布在不同地点且具有独立功能的多台计算机,通过通信设备和线路......