很久之前就希望能写下 OpenGL 学习的心得, 希望通过系列文章进一步让我对 OpenGL 的理解更上一层楼.
最初学习 OpenGL 的时候, OpenGL 主要通过固定管线来绘制管线, 即大量调用 glBegin/glEnd 这样的命令来绘制图元, 此时的 OpenGL 对新手来说还是比较友好的, 通过 glut 开发库, 几行命令就可以绘制一个三角形. 一般绘制图元的命令都是通过类似如下的代码实现
glBegin(图元类型);
glColor(...); //设置颜色
glNormal(...); //设置法线
glVertex(...); // 图元的多个顶点
glEnd();
这样写代码的问题在于绘制效率太低, 每一帧的绘制过程中, 需要向显卡端发送大量的绘制命令, 假设一个模型拥有上百万的三角形, 每绘制一帧, 为了绘制该模型, 我们需要发送上百万的 glVertex 命令给显卡, 显然极度地浪费 CPU 资源和时间.
当时为了解决这个问题, 推出显示列表这个概念, 该显示列表可以将一大堆的绘制命令封装起来并编译成一个绘制对象, 这样如果需要绘制该模型, 只需要调用该显示列表对象就完成了绘制.
但显示列表的灵活性不足, 如果一个物体的内容是静止不变的, 使用显示列表是一个比较好的解决方案, 但是如果需要频繁的改变一个物体, 例如物体的颜色发生变化, 顶点位置发生变化, 这个时候就需要重新编译显示列表, 而编译的过程是一个相对比较耗时的过程.
在这样的情况下, 现代 OpenGL 推出了 VAO 和 VBO 的概念, 主要思想是通过缓存对象保存顶点信息, 缓存内保存了顶点的位置, 颜色, 纹理坐标等信息, 这些信息会上传至显存, 最后通过 VAO 保存单个或多个缓存对象的布局. 绘制的时候, 通过不同的 VAO 调用不同的缓存布局, 使用简单的数条命令就可以绘制原来需要大量 glVertex 命令才能实现的功能. 具体 VAO 和 VBO 的使用将在后面的文章介绍.
对于最初的 OpenGL, 只需要会 glBegin/ glEnd() 命令以及一些设置矩阵的函数就可以了, 但现在 OpenGL 的发展, 尤其是 core 版本的 OpenGL, 已经摒弃了这种低效的做法, 并将大量原来的固定功能下放给程序员,即通过着色器来实现原来固定的功能.
这里举一个例子, 在以前只能使用固定管线的时候, 光照所使用的光源最多只能八个, 而且需要通过调用大量的 glLight 命令来设置不同类型的光照, 像有向光, 点光源, 聚光灯之类都通过调用 OpenGL API 来实现. 但是通过着色器, 我们就没有这样的限制, 你可以任意设置无限的光源, 但不同类型的光照就需要我们在着色器中写算法来实现了. 不再通过 glLight 这样的接口去创建和设置光源.
现代 OpenGL 学习曲线的陡然提高, 正是由于 VAO 和着色器以及更多新功能的引入, 使得大家要绘制第一个 OpenGL 程序变得比较困难. OpenGL 的这些改进, 都是为了渲染效率的提高, 在这里, 我希望与大家一起学习和进步. 下一篇文章讲 OpenGL 框架的搭建