前情提要:

上一篇: OpenGL 学习笔记 I:OpenGL glew glad glfw glut 的关系,OpenGL 状态机,现代操作系统的窗口管理器,OpenGL 窗口和上下文 OpenGL context_我说我谁呢 --CSDN博客 讲解了 OpenGL glew glad glfw glut 的关系,一笔带过 OpenGL 状态机,几句话讲解了现代操作系统的窗口管理器,理解了 OpenGL 窗口和上下文 OpenGL context 的概念。

重新备注一下,这系列笔记可以认为是基于 learnOpenGL 的个人复习补充笔记,主要是补充一些非机械式的直觉理解内容,比如为什么这样设置,我为什么要这样调 API,底层做了什么,以及一些他没有讲到的点,供我自己钻牛角尖用


这一段其实和图形学 OpenGL 没什么关系,复习可以跳过看下面的。如果复习 C/C++ 可以看一看

动静态库的复习:为什么要宏定义 GLEW_STATIC

  • GLEW 的实现有 static 版本和 shared 版本,不过公用一个头文件. 对于 shared 版本,一般需要链接到 so 文件(lib 在 windows)。实际 so 或者 lib 是怎么共享的呢?
  • 实际这个宏定义 is actually only needed for Windows platforms。下面解释这个事实。
  • 间接引用:ELF (PE)读入的时候,首先会解析符号表,一般来说,一层的引用(指针)很容易想到直接 map 好了共享库的地址空间之后,去把用到 shared 库的空位的给根据共享库的符号表给填好就行了,但是前面也说的,对于层层引用,根本是做不到直接写死的,比如你在dll的头文件里面 extern 了一个别的共享库的变量。因为链式加载库也是有顺序的,所以必须要根据 GOT 表来进行间接跳转。所以间接跳转是必要的开销。
  • extern 关键字:默认所有的非 static 函数都是 extern 的,即能被其他翻译单元访问。对于共享库,external linkage 保证引用了这个库(当然要经过头文件)的源码能够访问这些函数。这样的符号查询会推迟到链接时刻。对于共享库来说,链接的时候也要链接到 so 作为输入,只不过实际填写的时候是运行时。对于静态库来说,他直接被做掉了,因为静态库和生成的可执行文件一起包含翻译了。
  • extern 的查询顺序:ld.so 链接在 gcc 里面是 -l xxx 或者 -lxxx 对于在当前翻译单元能找到的符号,就会用在翻译单元里面。然后首先考虑(先绝对当前目录在走环境变量和系统目录)共享库的,最后考虑静态库的。默认的共享库以 so 后缀,静态库以 a 后缀。
  • 运行时:链接了之后 elf 里面就会有一个段存了他运行需要些什么共享库。这样 os 执行 exec 的时候,解析 elf,就能把共享库读进来然后 map 进虚拟地址空间。期间要做一些地址随机化的事情这里就不复习了。
  • M$ dll : 在微软这里的 PE 格式,类似 GOT 的东西是 IAT(import address table)实际来说就是 shared library(在微软是 dll)的 extern 都要坐在 GOT 表里面,然后运行通过间接访问来进行,至于怎么定义的就是加载库的 elf 段文件有定义一个 extern 变量(.data 数据段)的地方就把他地址写到整个程序的 GOT 里面。extern 这里其实是两用的, 导出定义用 extern,引入声明也用 extern。dll 比较特殊他的 dll 不是像 so 共享库那样默认全部都 export 的,而是要用 def 表来定义,或者通过__declspec(dllimport) 和 dllexport 对来实用。对于函数的 dllimport 默认的,对于数据来说必须指定使用 dllimport 因为 dll 的数据他不是放在 IAT 里面的,这是因为 windows 下的 dll 数据做了 CoW 处理。
  • 参考阅读:
    • dllimport 一定要用于变量(Window 下):LNK4217 - Russ Keldorph's WebLog - Site Home - MSDN Blogs (archive.org)
    • 全局变量机制不同:c - Why does Windows require DLL data to be imported? - Stack Overflow
    • dllimport 用于函数不一定要这样做:c++ - Why/when is __declspec( dllimport ) not needed? - Stack Overflow
    • linux 下共享库如何实现相似的可见性控制:Visibility - GCC Wiki (gnu.org)
  • 虽然讲了这么多,但是我并不打算用 glew,前面说了新的项目(不过这哪算什么项目啊...)可能使用 glad 是不错的。

使用 GLAD,轻装上阵

  • 前一篇文章没有解释为什么采用 glad,因为 glad 是一个你想要什么就生成什么的静态源码,默认自带所有 OpenGl 特定版本的 spec,然后还能自定义显卡厂商的 extension。https://glad.dav1d.de 通过这个网站就能生成 glad.c 和 .glad.h 文件,然后直接放进项目里配置好 CMake 就能用了,不需要安装,不需要考虑平台,下载 dll、lib 还是 so、a 文件。
  • GLAD 是一个纯纯静态源码使用的,非常好,我们直接用他。由此在不用区分什么 glew3s 和 glew3 以及要不要 define static 的区别了。
  • 需要注意的是我们说过 glew 和 glad 都是 loading library,所以实际使用是不会用到大量的 glad 函数的,而是就是正统的 glXXXX 的 opengl 函数
  • 不过 glad 就是太轻量级了,所以他必须(不是,实际你可以调用 wgl 或者 glx 的)配合 GLFW 使用。因为他要用到一个 GetProcAddress 函数(下面会讲为什么他在 glfw 里面),这个函数里面就是进行动态链接库查询的,然后返回函数指针。
  • glad 不需要文档,因为他的实际用途只有一开始 load,之后用的都是 glxxx 了,所以每次用只需要根据 glad 的 github README 把那几行 usage 给抄过来就行了

这里给出第一个 OpenGL 黑色窗口的代码

直接抄 LearnOpenGL 的和 glad README 的

// cpp 文件
//
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
GLFWwindow *window;
void glfw_chore() {// initglfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// create windowwindow = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);if (window == nullptr) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();abort();}glfwMakeContextCurrent(window);
}
void glad_chore() {// glad: load all OpenGL function pointersif (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;abort();}
}int main() {glfw_chore();glad_chore();while (!glfwWindowShouldClose(window)) {glfwPollEvents();std::cout << "One" << std::endl;glfwSwapBuffers(window);}glfwTerminate();return 0;
}

GLFW Window OpenGL context

  • 上下文:上一篇说过了对于 OpenGL 来说,状态机的状态是针对一个Context 来设置的,这个一个 Context 需要包含一个窗口的缓冲区,从而让 GPU 渲染到窗口的 framebuffer 上去。存在形式是一个全局变量挂到那个上下文上,所以是通过 MakeContextCurrent 这种名字的函数(实际 OpenGL 没有规定这个接口,由操作系统和显卡厂商共同实现)指定了之后就针对那个上下文进行操作。
  • 管理窗口的钩子:对于 GLFW 来说,他管理的是一个操作系统的窗口的交互然后架起到 OpenGL 的桥梁。所以实际他管理的对象是窗口,存在形式是通过一个窗口指针来指定要设置的窗口。实际一个窗口在 glfw 里面是一个很复杂的结构体,包括了窗口的各种属性。
  • : 因为我们不直接访问暴露的 framebuffer,实际因为我们通过 glfw 管理缓冲区(一个窗口一个),所以实际调用的 glfwMakeContextCurrent 来设置,里面会调用 OS、驱动 的 makecontextcurrent 的。这么看来,glfw 窗口就是我们以后管理 OpenGL 上下文的东西了,之后操作上下文的行为通过 glfwxxx 函数进行调用。而涉及在当前上下文上进行渲染、数据传送等操作就都通过 glxxx 函数进行
  • 配置与 getProcAddress而为什么对整体的 OpenGL 参数进行设置(比如核心立即模式和最小最大版本号这种设置)以及加载函数指针也通过 glfw 提供呢?我们其实知道 getProcAddress 是 x window (linux)和 wgl (win)提供的,这是因为整个 GUI 都是基于 OS 的,就算 OpenGL 想要直接写显卡framebuffer 使用像素图形模式功能,也需要透过 OS 查询显存映射地址,查询驱动本身也走 OS 查询,所以这部分直接就做在 OS 的窗口管理器接口下面了,所以归入 glx 和 wgl 函数簇下,对于使用跨平台的 glfw 的我们来说,自然这些工作就通过调用 glfw 来做了。

VSYNC 垂直同步

  • 垂直同步的好处和坏处,对不同游戏的适用范围玩游戏的时候其实肯定都学习过了,这里就不说了。我们从 OS  调度的方向来讲,VSYNC 有一个好处是很明显的,那就是减少 CPU 的 usage。至于具体的 gameloop 和各方面的分析,还涉及 gameloop 的写法,游戏逻辑时间、逻辑帧和渲染帧等概念,如果是网游还涉及网络质量问题,这个前面有三篇学游戏理论的时候的笔记我分析过了(其中一篇:游戏开发基础笔记:逻辑帧和物理帧辨析 | Gameloop | 游戏循环),不赘述。
  • 我们观察基本代码的运行结果,可以发现,打印的速度好像是一卡一卡的,这就是因为他启动了垂直同步,这样 CPU 就不会 100%(指一个 core ,实际可能 25% 或者 12.5%)了(主要是我回想 SDL 写的 RoboCat 游戏 gameloop 必100%,而 learnOpenGL 教程又没讲这一点,我忍不住)。
  • 最简单的 gameloop 里面,PollEvent 做的事是简单调用 windows 系统的消息模块,非阻塞 peek 全部已经 queued 的事件,然后调用 callback 之后返回。实际 callback 的事情可能什么都不做,或者不会太复杂,我们一般会把复杂的逻辑放到逻辑帧里面用一个 processInput()  wrapper 来处理所有我们关心的事件(比如通过 getKey 这种名字的函数)。这一点和网络编程是差不多的,因为 callback 一般是 stateless 的,不可能让他处理完全部东西,这个和 js 那种恶魔语言不同。对于需要急速捕获用户连击事件的话,用 callback 应该是更适用。总之一种(callback)是异步无状态的,一种是 backlog 自己 dispatch 输入,还是看用途。
  • 所以实际 vsync 部分发生在 swap buffer 里面。首先glfw的文档的确表明这个 If the swap interval is greater than zero, the GPU driver waits the specified number of screen updates before swapping the buffers. 在  window 下,我 step into glfw_swapbuffers 最后发现调用的是 wingdi 的 swapbuffers。查阅我没有看到具体实现为什么会引发 interval,而且他另外还有一个 swapinterval 函数,结果发现 swapinterval 底层 wglSwapIntervalEXT 这个函数其实是设置 interval 的意思,而不是和 swapbuffers 并列的。所以是通过  swapinterval 影响 swapbuffers 。吐槽 glfw 的文档搜索功能稀巴烂(可能是我不会用)。
  • 关闭 VSYNC需要注意 OpenGL (实际是 OS 和驱动实现)默认开启了垂直同步。我们当然是直接操作 glfw 设置各种和 context 有关的参数,所以就调用这个函数就行了:glfwSwapInterval。附带的参考连接是:I have a question about fps - support - GLFW opengl - My limited FPS : 60 - Stack Overflow
  • 实现一个 FPS 显示:An FPS counter (opengl-tutorial.org) 这种做法应该所有游戏里面通行的做法,实际显卡驱动提供的或者某些第三方插件(游戏盒子什么的)则应该是在操作系统、驱动里面进入显卡 render (每个渲染帧)之前挂上这段代码就能实现帧数显示了。

OpenGL 学习笔记 II:初始化 API,第一个黑窗,游戏循环和帧率,OpenGL 默认垂直同步,glfw 帧率相关推荐

  1. OpenGL学习笔记(2)第一个程序——犹他茶壶(Teapot)

    好了,python+opengl的开发环境搭建好后,我们就可以开始学习了. 这里,我们先学习一个常见的例子--犹他茶壶. 先贴代码

  2. OpenGL学习笔记:矩阵变换

    文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...

  3. 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

    ✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...

  4. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

  5. OpenGL学习笔记(八):进一步理解VAO、VBO和SHADER,并使用VAO、VBO和SHADER绘制一个三角形

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7888 ...

  6. OpenGL学习笔记(一)绘制点线面及多面体

    OpenGL学习笔记(一)绘制点线面及多面体 绘制点线面 #include <iostream> #include <GL/GLUT.h> #define PI 3.14159 ...

  7. OpenGL学习笔记(一):环境搭建、三维空间坐标系理解以及OpenGL的基本使用

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7866 ...

  8. OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...

  9. Redis学习笔记 - 数据类型与API(1)Key

    Redis学习笔记 - 数据类型与API(1)Key Key相关命令 1. 常用命令 命令 含义 时间复杂度 keys 查找所有符合给定模式 pattern 的 key O(N), N 为数据库中 k ...

最新文章

  1. DCMTK:读取DICOM图像,添加一个Curve并将其写回
  2. ipc$开启telnet
  3. linux lnmp yum版安装
  4. keyloadtool_phoenix 利用CsvBulkLoadTool 批量带入数据并自动创建索引
  5. mysql connector开源协议_Spring Boot 集成 MariaDB Connector/J 连接 MySQL
  6. hibernate的环境搭建
  7. HDFS伪分布式集群搭建
  8. MySQL性能优化的21个最佳实践 和 mysql使用索引
  9. 我的Java秋招面经大合集
  10. html文件导入奥维,【干货】奥维地图 | 如何导入高程数据
  11. 希捷2T硬盘ST2000M001分区读不到变为RAW修复记
  12. MySQL修改数据表中的字段名_MySQL修改数据表中的字段名
  13. Spring实现统一捕获接口层异常与邮件警报
  14. vue之封装loading组件
  15. 区块链入门教程(4)--搭建单机四节点FISCO BCOS联盟链
  16. hibernate配置映射的问题
  17. 设计模式深入学习---Decorator装饰模式(结构型模式)
  18. 面试小结一:关于操作系统的面试题整理
  19. 计算两个时间戳之间的自然天数
  20. 前端基础——css进阶(三)

热门文章

  1. 在Ubuntu终端中设置中文输入
  2. 小程序与普通网页开发的区别
  3. 淘宝api 万邦Alex 19970108016
  4. 学习人大金库和达梦有感
  5. 《圣经》中最让人感动的十句
  6. 湖南大学计算机系院士,与院士面对面 | 名家共话“未来计算与产业变革”
  7. 去掉IE自动弹出“该页正在访问其控制范围之外的信息,这有些危险,是否继续?”对话框
  8. 分享一篇教你掌握英文论文写作技能的文章(来自MIT博士)
  9. 攻防世界-wp-MISC-新手区-12-功夫再高也怕菜刀
  10. 苹果手机软件升级密码_因为苹果的数据安全保护 让我丢了数据