从零开始学习openGL与GLSL(没有计算机图像学基础),开始确实挺费劲,网上的资料虽然多,但不系统,例子也不全,openGL还好(这里指的是v2.0之前的版本,使用glBegin(), glEnd()方式绘制),完整的例子比较多,而GLSL的例子相对少不少,中间走了不少弯路。下面说一下个人的一些学习经历和看法。

1. 学习路线
GLSL的学习还是比较推荐以红宝书为主线进行学习,其他资料辅助,虽然入门难度比较大,但是比较系统权威,不会走偏,不懂的话多练练,多翻翻其他资料,也是可以慢慢弄清楚的。

大家可以自己去找红宝书《OpenGL编程指南(第八版).pdf》(与之配套的源码《oglpg-8th-edition》 )。蓝宝书《OpenGL超级宝典》也是经典,大家也可以看看,都比较权威,正如他们的名字,红宝书重编程,蓝宝书重原理。

个人的学习经历是:

1. 先把网上的老的openGL程序都跑一遍(使用glBegin(), glEnd()方式绘制 ),先看效果,再捋概念,熟悉老的程序的话,学GLSL会快很多,概念是一致的,只是编程实现方式不同。

2. 中途恶补《计算机图形学》知识,熟悉齐次坐标,坐标变换等知识。

3. 一章一章看红宝书,对照sample写自己的代码。

4. 再根据学习的情况,一步一步往自己的工作中需完成的任务上靠。

 

说明:关于红宝书,这里推荐大家以此为主线进行学习,估计很多人有意见,入门难度确实稍高,第一个程序,前面的4章讲完了才能基本搞懂,都不像一般的helloworld程序一样简洁,我也是搞了好久才把第一个例子跑起来,搞明白,而且sample程序里问题不少,根本没法直接用,可能是我下载了假的sample程序,做参考还是不错的,后来我放弃了直接跑sample,而只是参考,自己改写,这样理解更透彻。推荐的理由是系统性与权威性,而不是门槛高低,所以本文的目的不是替代书籍,而是为大家提供一些有意思的代码,让大家更好的理解红宝书。

2. 基本概念
2.1 顶点、片元着色器
刚接触openGL,新概念确实不少,其中顶点着色器这个概念搞了好久都没弄明白,老是想着顶点着什么色,其实这个都是翻译误导的,英文为Vertex Shader,Shader这个词翻译为着色器不好理解,不过这东西确实不太好翻译,反正顶点着色器和上颜色没什么关系。

 

这里从网上找了一张图,可以这么理解,顶点着色器的作用是做出石膏模子,而片元 着色器的作用是为石膏模子上色,简单的说,顶点着色器是做出3D图像的轮廓框架(毛坯,进行空间与空间变化等与空间相关操作),而片元 着色器让它变得好看一些,即上色(装修,进行颜色与纹理等操作)。片元着色器(fragment shader)有的又称为片段着色器,只是翻译问题,我们以红宝书的名称为准。

 

2.2 GLSL的CS模型
CS(客户端服务器)模型,client是gl打头的一系列api,对应的时CPU部分,而server就是GLSL语言部分,对应的时GPU部分。

老的api编程对用户来说,这个概念不明显,api已经封装了GPU部分操作,在用户看来,感觉不出来哪些操作在cpu上执行,哪些在GPU上执行。这里举一个例子来辅助大家理解。

 

上图是计算器和美元,用计算器算账,做以下类比:

1. 客户端:人的脑和手(数据输入,灵活,但计算速度不如计算器),计算器键盘是计算器提供给用户的接口(api)。

2. 服务器:计算器,单纯运算能力比较强(GPU)

这就是固定管线模型,我们输入的数据(一天的账目),计算器很快的给出结果,比自己算快多了,GPU初期就是这样的。

计算器用来算账是不错,但我有一个需求,我要求每一笔账目输入的是美元单位,而显示出来的是人民币单位,有两种方法

1. 输入账目的时候每一笔都乘一下汇率再输入(CPU处理),但这样,整体速度就慢太多了。

2. 也可以专门出一款计算器搞一个功能键(创一个新的api)来做这项工作,但用户的需求是多种多样的,这样的灵活性太差。想一想老式的中文打印机,每个字一个键(区位编码),用起来与扩展起来都非常不方便。

此时的计算器就不满足需求了,我们需要可编程计算器(电脑),我输入数据过后,还可以控制输出数据的时候再乘一个汇率,这就是可编程管线。

可编程管线简单的说就是,用户CPU输入数据过后,GPU还可以对数据做进一步编程处理,CPU只需要输入必要的数据,剩下的需求都交给GPU来做,充分利用GPU的处理优势,通过缓冲对象,进一步提高处理性能。CPU侧重点在数据输入与控制,GPU着重数据处理与显示。

有的网友问,为什么《OpenGL编程指南(第八版).pdf》例子没有用到glBegin(), glEnd()的方式绘图,就是因为,红宝书8的例子全部用GLSL shader编程实现的,这些老的api新的版本已经废弃(但还是可以使用),这里建议大家还是先熟悉一下老的api,类比起来有助于理解。

2.3 归一化
归一化实际是把对应的数据类型转成[0-1]的范围,如使用GL_UNSIGNED_BYTE 类型存储该颜色分量值, 归一化是把0~255转为0~1的范围,如RGBA中R的值为10,即颜色分量R归一化公式为R/255 = 10.0/255,有符号型数据的归一化会复杂一些,计算公式红宝书上有讲(3.3.1章节)。

openGL的坐标系也是一种归一化的坐标系(规格化设备坐标系),在传入坐标时,api也经常有是否归一化的选项,注意归一化是根据数据类型长度来的,同一个值,不同类型的归一化完全不一样。这个特性要用,如果给出的是非归一化的坐标,如BMP中的RGBA的值,最好使用接口去归一化,用CPU归一化效率太低。

openGL坐标范围为[-1.0,1.0],但不是说坐标只能是这个范围内的,超出范围后,只会显示不出来而已,通过缩放,平移,旋转等操作,可以把超出1.0的坐标显示出来,颜色RGBA的范围为[0,1.0],这个范围不能超,否则没意义。

shader里面的颜色用的是RGBA,注意透明度分量A在最后。

 

2.4 齐次坐标
为方便坐标转换,GLSL中用的坐标全是齐次坐标,即(x,y,z,w) 4维坐标,坐标变换的矩阵也是4维矩阵。

齐次坐标(x,y,z,w)转3维坐标(x/w,y/w,z/w),

 

2.5 向量
向量和点的表示方式比较类似,点主要是表示位置,向量主要表示方向。

点是三维空间中的某个坐标,是绝对的,它的值是参照原点的,而向量用于表示力和速度等具有方向和大小的量, 通常用具有长度和方向的线段来表示,虽然他们都具有三个分量,但对于向量,如果将向量放在坐标系中的任何位置(平移),都不会改变其性质,因为向量表示的是方向和大小,与位置距离无关,它的值是相对与基准点的。

齐次坐标与向量在shader编程中大量使用,还有矩阵的概念都需要弄清楚。

2.6 缓冲对象
openGL中有大量的缓冲OBJECT,常见的有vbo,vao,ebo,pbo,fbo,这些缓冲对象开始学习的时候不是必须的,对openGL有一定的了解后再去深入理解即可,使用缓冲对象能使程序效率更高,不使用也能用。红宝书第一个例子就抬出了vbo和vao,确实增大学习难度,理解了过后,这些概念也没那么难,但由于使用方式习惯(gen bind方式)与一般程序使用方式习惯不一样,又夹杂着一堆的新概念、新的api,学习难度就上来了。

本文档后续用完整例子来讲解缓冲对象的用法。

3. 其他说明
系列学习文章都是针对GLSL编程的,基于glsl3.3版本。

后续的例子尽量不依赖太多内容,程序尽量完整,尽量能直接跑,用的C语言,应该可以跨平台。

学GLSL之前,大家还是把老的openGL程序先熟悉一下(使用glBegin(), glEnd() 绘制),环境搭建与辅助库的使用大家自己去了解。可以边学边查。

不清楚的地方,可以一起交流。