开端
老早就打算学学 OpenGL 了,只是因为各种原因一直没有系统地去学习。
在去年时,发现 Gnome 默认切换到了 Wayland 模式,于是自己想着干脆趁机体验了一下传说中的 Hyprland 和 Niri,发现折腾起来确实挺有趣的。
后来在尝试动态壁纸软件时,误打误撞发现了一个利用 MPV 将视频作为壁纸的软件,以及一个可以把 Shader 作为桌面壁纸的软件,感觉非常神奇。稍微看了看源码,发现 Wayland 的 Layer Shell 协议居然支持使用 OpenGL 渲染内容。既然都可以使用 OpenGL 了,那么渲染 3D 模型肯定也不在话下,抱着这样的想法想着感觉正好可以利用这个点子学学 OpenGL 和 Wayland。
OpenGL
虽然 Vulkan 距离问世已经也有十年了,但考虑到学习曲线的差异,从 OpenGL 开始来学习图形 API 是一个比较适合入门的选择。以及 OpenGL 还有丰富的学习教程。很多年前我就收藏了 LearnOpenGL CN 这个网站,它已经在我的收藏夹里默默吃灰很久了。作为目前公认的最好的现代 OpenGL 教程,从这里开始是再合适不过了。
其实之前我也断断续续地看过一点 LearnOpenGL CN 的教程,但每次都卡在坐标变换和线性代数这块,于是又决定先从《计算机图形学》(仙人掌书)开始看起。不得不说,后者的几何变换的章节确实是很扎实地一步一步地从推导公式讲起,让我有种自己也能理解了开窍了的错觉。可惜看起来实在是有些枯燥,极大地消磨了学习的热情,导致我反复了几次也一直没能坚持看玩投影变换的那章。
后面我意识到可以先不必去纠结投影矩阵是怎么生成的,而是先接受它然后用起来。事实上 LearnOpenGL CN 的教程也是这么做的,它将底层的数学细节用 GLM 这个库给隐藏了起来,从而让读者更容易入门。不得不说,对于从零入门的学习者而言,有时希望打好基础却反而会陷入到深坑里面。不妨先接受自己没能完全掌握的事实,等自己熟悉这个领域之后再回头学习也未尝不可。
以前曾在论坛看到过有人分享自己的学习经历,用了“腌制”这个形象的比喻。一直泡在相关的内容里,最后发现自己就像是被泡入味了泡菜一般,发现之前一直很陌生的概念也逐渐熟知了。感觉自己也渐渐体验到了这种感觉。
glTF
我的目标是在 Linux 桌面上渲染 3D 模型,因此首先就得决定自己要支持渲染什么格式的模型。排除了过于简洁的 obj 格式以及比较冷门和专有的格式之外,glTF 基本上就是最适合我的模型格式。
glTF 是 Khronos 工作组制定的一项 3D 模型格式规范,GPU 友好、API 无关并且易于分发。glTF 规范包含了两种模型格式,.gltf 和 .glb。而前者基本上就是一个普通的 JSON 文件,包含了渲染 3D 模型时所需要的顶点、图元、纹理、变换、动画等数据。这非常适合开发和调试时让开发者直接阅读模型的内容。你甚至可以在必要时用文本编辑器直接修改模型文件,可以说是非常方便了。
考虑到用 C 语言直接处理 JSON 文件以及解析模型里 accessor 和 buffer 的数据还是很繁琐的,我直接使用了 cgltf 这个库,只要引入一个头文件就可以轻松实现模型的加载。这样也可以将更多的精力投入到渲染的部分上面。
PBR
PBR 全称是基于物理的渲染(Physically Based Rendering),听名字就知道这个功能少不了一堆令人害怕的数学公式。不过在跟着教程走了一遍之后,发现似乎不是想象中那种完全理解不了的难 —— 虽然的确还是有很多理解不能的公式,但它们的物理意义、它们的目的都大概了解了。有种囫囵吞枣的感觉,但总比一无所知要好得多。
由于一开始是跟着教程来实现 PBR 的,后面在需要实现 glTF 规范 extension 中定义的功能时,发现 extension 是基于 glTF 规范内的 PBR 公式来描述自己的,这导致我完全不知道应该怎么对应的修改现有的 PBR 代码。因此我不得不回头重看了一遍 glTF 规范有关 PBR 的内容,好在原理是一致的,只是计算过程和方式有些出入,在 AI 的帮助下捋了捋官方实现的示例代码,总算是知道该如何去实现 extension 里的功能了。
Render Doc
一直都有听说渲染的 BUG 很难排查,感觉确实如此。Shader 代码并行运行在 GPU 上,一般来说做不到像 CPU 那样轻松地单步调试代码。
开发过程中经常发现自己的实现渲染出来的效果很奇怪,但一时又找不到原因,既不能 log 输出日志,也没办法下断点单步调试。网上介绍的将数据作为颜色输出的技巧理解起来又确实过于抽象了。
好在还有 Render Doc 这个工具。虽然用起来磕磕绊绊不太熟悉,不过查看渲染过程中数据状态的功能是实打实的好用。记得遇到过一个邪门的 sRGB 颜色空间 mipmap 生成的问题,要是没有 Render Doc 的话,我估计能卡在那里一辈子。
未完成与 AI
这个项目其实还是未完成。从去年末今年初的心血来潮开始开发,到中间决定休息一下,一直到现在完全没动力再继续了,可以说是拖了好久了。一开始其实是打算再加上非骨骼动画的动画实现以及自渲染 UI 的。结果在为了实现动画功能而重构完项目之后,懒癌发作一搁置就是好久。期间试着用 AI Agent 开发了好几个玩具,然后就再也没有动力古法编程继续开发了。就连这篇文章也是拖了好久,本来都不打算继续写了。
AI 切切实实地拓宽了个人的能力边界,让人感想敢做以前觉得很麻烦的事了。一开始个人感觉 AI 可能只是加快了知识的流动速度,到现在 AI 已经可以脱离人类完成复杂的任务了。而肉眼可见的,我们现在才仅仅是站在了起点。
再见了,古法编程
总之,再见了,古法编程。以制作玩具为导向的学习路线是很有趣的,而让 AI 直接为你制作玩具则更加有趣。学习不会消失,以学习为目的的古法编程还是有存在价值的。只是摸鱼一阵子之后发现 AI 已经把代码交付好了还是太有吸引力了。