OpenGL 学习笔记 I:OpenGL glew glad glfw glut 的关系,OpenGL 状态机,现代操作系统的窗口管理器,OpenGL 窗口和上下文 OpenGL context

本文主要是介绍OpenGL 学习笔记 I:OpenGL glew glad glfw glut 的关系,OpenGL 状态机,现代操作系统的窗口管理器,OpenGL 窗口和上下文 OpenGL context,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本来想边速成 OpenGL 然后顺带复习图形学除了光线追踪部分来准备考试,但是考试推迟了,所以就不速成了。首先是一开始配环境遇到的各种问题,之前第一次学 OpenGL 照猫画虎复制老师给的源码画了line ,strip 多边形等的图形,实际没明白 OpenGL 到底是这么设计的,还有  glew glad glfw glut 这些东西也很多一笔带过要么说是解决一些 OpenGL 的附带问题,加载函数和绑定窗口。我没找到很具体的解释,所以还是自己亲自部署一下了。

对于这些理解,感觉源码面前了无秘密还是有必要的:这里有答主给了一个把 glad 和 glfw 全部工作都展开出来的代码(linux 下 的 xwindow):

glfw和glad有什么区别呢,glad是用来干什么的(只知道是一个库)? - 知乎 (zhihu.com)l

 Windows 平台的空 OpenGL 窗口创建的完整的代码这里也有:c++ - Initializing OpenGL without libraries - Stack Overflow,读者可以参照阅读。


复习编译与链接装载 - 动态链接与 PIC

  • PIC 是地址无关代码的意思。地址无关代码主要是在共享库里面的因为库的函数、变量地址都是运行时才能确定的。对于库自己内部的寻址由于可以直接用相对寻址,比如基于 PC 的相对 offset JMP 寻址。
  • 全局变量和共享依赖:但是对库里面又引用了其他的模块或者库以及每个调用程序用到的全局变量(必须独一份),都需要去查询他的调用地址(say 一个函数指针从而可以调用函数)。
  • 动态链接:GOT 表是全局位移表,通过这个表连接器在 elf(executable and linking file) 读入运行的时候装载。
  • 性能:这个动态库的运行是慢一点的(多一次间接寻址)。
  • Procedure Linkage table lazy load 提高程序的启动速度,但是这样 lazy binding 实际运行时可能会慢,但是没用到的不用 pay for it。
  • 编译方法:gcc -fPIC -shared a.c -o liba.so
  • 平滑升级:libxxx.so 和 libxxx.so.1和 libxxx.so.1.x 解释,是因为实际库是有版本的,Linux上对动态库的命名用libxxx.so.a.b.c的格式。然后 so 是软连接到 so.1,so.1 是软连接到 .so.1.x, 这样可以实现现场升级。如果 so 连接到 .1 的话,运行时可以升级到 .2,然后因为原来文件是软连接,所以可以保留 refcnt,新的程序可以运行 .2。实际更新动态库可以采用 rm 源文件然后新建一个,这样是两个 inode 互不影响。windows 的文件被占用无法删除就做不到这一点了。

接口、显卡驱动和 dlsym

  • Open GL 是接口与规范(spec):Khronos 是超原始神时空旅行者,他设计和规定 API,不指定实现。一开始的 1.1 版本经典地 shipeed with OS。其表现为为一个没有实现的头文件。在 windows 下是在 SDK 的 include 文件夹的 um/gl/gl.h,这里 um 是 user mode 的意思。(理论上应该在 GL/gl.h,但是历史原因 dos 不区分大小写)。gl.h 里面只有一些 macro 定义和 1.1 的经典 OpenGL 函数签名。但是没有实现,实现由显卡驱动完成。
  • 显卡驱动:由于图形学管线的东西不是简单的通用计算模型,硬件上也和 CPU 架构不一样(GPU Architecture 可以读 RTR4 Chapter23),每种不同的显卡版本的架构和他的操作也不一样,但是总的来说是支持各种基本的图形管线操作和各种新增特性的,所以没有 GPU 的汇编语言,GPU 厂家直接提供驱动,包括 Open GL、Direct X ,Vulkan 的 runtime library等。(OpenGL 某个版本之后不再进行维护,超原始神时空旅行者将会转向维护 Vulkan,作为 OpenGL 的现代替代品,他更加底层,驱动变薄,灵活度高)。
  • 链接库:由此可知实际编译 Open GL 程序必须要链接到显卡驱动提供的 libGL 库。一般显卡驱动安装后,会在 /usr/lib/ 下面创建一个软链接 libGL.so pointed to implementation.
  • OpenGL extension由于硬件的问题,而 linux 和 windows 系统都必须提供必要的向下兼容,对于旧版的显卡,如果驱动没有提供新版本 OpenGL 引入的,OpenGL 无法直接更新其 gl.h 接口,否则对于驱动(a.k.a. libGL.so)没有提供的,无法链接。所以都只能通过动态绑定来实现。extension 还能够支持类似 cpp TS 的功能,对于没有直接进入规范的 api,如果显卡厂商提供了,也能通过 extension 机制来实用。
  • dlsym linux 下(win 是 LoadLibrary)可以通过 dlsym 来 dynamic linking 在一个 so 文件(which is opened via dlopen)里面查询一个符号。通过这个,应用程序可以根据 OpenGL 1.1 之后的 spec 规定的函数名字或者一些显卡可能提供的 extension 来进行 glXXXX 函数的查询,如果有就不会返回 NULL,这种运行时查询机制比直接头文件全部指定一些可能不存在的函数要靠谱不少,而且也支持多文件查询,比如可以优先选择某个实现,如果不存在,就 fallback 到另一个实现上。
  • GDI 是什么?GDI  是微软的 graphics device interface。一般来说,GPU能做的事情 CPU 也能做。对于 2D 的东西来说,其实CPU也能胜任(漂亮的 GUI 会有很多浮点运算,不过如果不要太多炫酷效果,实际整数也够用了,想各自画图算法 Breshenham 都是整数算法),GPU主要是 3D 很厉害。

What is GLADGLEW

  • Loading library这两个都是上面说的,完成动态链接库的现场绑定的东西。
  • 同样的:glad 和 glew 做的事情是支持 OpenGL 1.1 之后的函数的动态加载,所以叫 loading library。
  • Which one更加新写的库,他和 glew 各自都有优点缺点,新程序可能都用 glad 了。但是注意的是 glew 和 glad 都支持最新版的 OpenGL(4.6)。glad 可以直接 ship,glew 可能要在系统 include 设置(以库提供)。

窗口化和 OpenGL 上下文(Context

  • 首先理解没有窗口化时期的 GUI早期的 OS 一般是单一的 console 输入输出对于 linux 下是怎么开机 init 和 login 的,之前  Proactor Reactor 模型 里面分析过了。这里主要要理解的 console、terminal(shell 就不用说了,shell 是一个处理文本命令转为 exec 或者 syscall 的程序,非GUI 的UI)。terminal 可以概括为是一套环境,包含文本 IO,或者说他是一个程序,负责 IO 与 shell 互动。
  • 显卡的 framebuffer OS 的显示缓冲区:实际,早期的系统其终端显示的字体是由显卡来实现的。这种模式在显卡上叫做文本模式。相对的是图形模式,图形模式的 framebuffer 就是一个分辨率(e.g. 4096*2160)的 2d-array,而文本模式,会根据字符大小宽度计算出最大行数和列数。所以说 DOS 游戏其实就是工作在像素模式,显存是像素。这也是为什么 dos 进游戏可能会黑屏一会儿,因为从文本模式转像素模式。而 window 进游戏是 window 窗口管理器直接把全屏缓冲区送 OpenGL 程序或者 Direct X 程序,所以换缓冲区可能会黑屏一小会,而窗口化不会但是窗口化还要走一层窗口管理器再渲染,性能变差。
  • Windows terminal但是如果走了窗口化的路子,就不能依赖显卡的文本模式了。我们查看最新的 UWP 应用 windows terminal  的源码(他是 CPP 写的,说是 GPU 绘制,好看快速)里面,renderer 有两个渲染器,一个是 GPU 的一个是 gdi based 。
  • 如何实现一个窗口管理器:比较抽象的方案是通过提供一整套组件库等上层抽象(WPF、win32 GUI 程序),比较底层的方案是提供一系列 frame buffer 的管理器。应用请求生成一个 window 的时候,就给他一个 framebuffer(OpenGL、Direct X 应用程序的方案)。对于窗口切换和 resize 等东西,都用事件驱动来做。每次通过计算激活窗口,进行 Z-buffer 遮挡测试,临时缓冲区等方案。还有 diff 等进行合并,以及添加阴影特效等。他很复杂是因为比如你开一个窗口一直需要实时更新的(比如一个计时器一直更新),就算他不是 active,也要实时渲染做 composition 的。每次鼠标移动窗口的时候,要重新做 z-buffer depth 测试。
  • OpenGL context这么一来,就能明白了,OpenGL 无法提供窗口管理的功能,这种东西一般都要 OS 提供。 Linux 下通过 x window 系统,window 下是 wgl。所以读者可以在上面的源码中看到一个是调用了 glx 的东西,一个是调用的 wgl 的东西,其实这些就是 OS 针对 OpenGL 做的一些窗口 handle 的抽象接口。窗口在 OpenGL context 的存在形式是一个 framebuffer 的描述信息。而用 context 本身是一个状态机模式,整个 OpenGL 就是一个巨大对象,操作不通过对象指针进行,而是全局的 context 指针,所以 Open GL 是通过 setContext 来切换所有函数的操作对象的。OpenGL 的函数也没有返回值,这个是经典的 C style 了,因为返回一个对象很复杂,所以一般用参数同时负责 in out。

What is GLFWGLU

  • glut 是 utility,他提供了上面说的创建窗口和上下文的事情,防止要自己做一堆脏活。还顺便把键盘鼠标这些和 windows /Linux 的窗口系统的事件负责的东西完成了。glut 还提供了画立体图形、茶壶等的函数,不用自己写 glBegin 和传顶点什么的。最后一次更新是很多年前。
  • glfw一个比较新的还在维护的库。It provides a simple API for creating windows, contexts and surfaces, receiving input and events. 他不止支持 OpenGL 还支持 Vulkan (不过 Vulkan 名义上是 OpenGL 的正统不向下兼容的后继)。

OpenGL 状态机

  • 不用指针:为了方便,OpenGL 用一种 id (GLuint)来表示他的对象,类似 Linux 下管理文件是通过 file descriptor,实际这些东西都是他自己内部来 malloc (只是比喻,实际是在 GPU 的缓存)好的。像顶点数据,以及缓冲区所以外面又套了一层 glGenBuffers、glDeleteBuffers 这些成对的东西。对我来说,永远支持 RAIIc++ - RAII wrapper for OpenGL objects - Stack Overflow
  • 缓冲区:GPU 与 CPU 的交互由于没有 UMA,这样程序必须通过 CPU (或者 DMA 类似的东西)往 GPU 传数据,所以像 Buffers 就是 GPU 的 buffer,至于什么时候传给 GPU 就看实现了。
  • BindOpenGL 的显卡上 Object 还真的很 C style 的,就是真的是 malloc + free + void* 的比喻,对于实际空间要设置为什么类型是通过宏来指定的,所以要 Bind 来指定类型,相当于是你还要给 void * 指定一个数据类型。
  • 为什么不用经典 OOP实际现在 core 模式的 OpenGL 是基于三角形、顶点数据以及 shader 来实现整个流程的,所以 OOP 的发挥没有意义。(当然是支持直线和各种多边形的,但是模型表示一般都用三角形顶点集合和片元集合,所以 OOP 没有什么意义,比如你 Triangle t = new Triangle 然后 draw,实际用途不大)。像 RAII wrapper for buffers 或者 textures 会优点意义,因为类似管理 malloc 和 free。

这篇关于OpenGL 学习笔记 I:OpenGL glew glad glfw glut 的关系,OpenGL 状态机,现代操作系统的窗口管理器,OpenGL 窗口和上下文 OpenGL context的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/970550

相关文章

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

详解Java中三种状态机实现方式来优雅消灭 if-else 嵌套

《详解Java中三种状态机实现方式来优雅消灭if-else嵌套》这篇文章主要为大家详细介绍了Java中三种状态机实现方式从而优雅消灭if-else嵌套,文中的示例代码讲解详细,感兴趣的小伙伴可以跟... 目录1. 前言2. 复现传统if-else实现的业务场景问题3. 用状态机模式改造3.1 定义状态接口3

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

PostgreSQL中rank()窗口函数实用指南与示例

《PostgreSQL中rank()窗口函数实用指南与示例》在数据分析和数据库管理中,经常需要对数据进行排名操作,PostgreSQL提供了强大的窗口函数rank(),可以方便地对结果集中的行进行排名... 目录一、rank()函数简介二、基础示例:部门内员工薪资排名示例数据排名查询三、高级应用示例1. 每

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.