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。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值