OpenGL 随想(五):从 Fixed-Function 到 3.2

国庆之后一直没写 blog。一来其它事情很多;二来闲下来就想阅读。长假还算没有荒废,以 OS X Lion 支持 OpenGL 3.2 为动力开始重启 OpenGL 的研究。假期前两天在基于 OpenGL 2.1 的产品中加上了用 GLSL 1.1 写的 bicubic interpolation 的 fragment shader,给学习 OpenGL 2.1 画上一个句号。接下来用其余的假期调通了第一个 3.2 core profile 程序。这个假期可以说是从主要基于 fixed-function pipeline 的旧 OpenGL 向完全可编程 pipeline 的新 OpenGL 前进的一步。

从 OpenGL 2.1 到 OpenGL 3.0 是一道分水岭。在其后 OpenGL 开始致力于抛弃之前版本中只适合过时硬件结构的 API,让开发高质量高性能的 driver 变得更容易,从而带动整个 OpenGL 平台的改善。到 OpenGL 3.1 之后,虽然不少实现仍然保留旧功能(nVidia 的 Windows driver 号称永不去除任何功能),但是在同一 OpenGL context 中混用二者越来越困难。OS X Lion 允许创建两种 context:OpenGL 2.1 和 OpenGL 3.2 core profile。前者只能通过 EXT 使用 3.x 有限的部分功能,后者可以使用 3.2 的所有新功能(以及通过 EXT 使用更高版本的部分功能),但无法使用 2.1 的旧功能。

被抛弃的最大部分是立即模式 (immediate mode),也就是初学者熟悉的 glBegin/glEnd/glColor/glTexCoord/glVertex 等等函数,以及和矩阵相关的功能。再者是去掉了原来固化在 driver 或者硬件中的坐标变换、光照等等 fixed-function pipeline 功能,转而必须由 shading language (GLSL) 编写的 shader 完成 (OpenGL 2.1 里可以用 shader 来替代 (override) fixed-function pipeline,但并非必须,在没有 shader 的时候 fixed-function pipeline 依然工作) 。我认为去掉立即模式的原因是 GPU 能直接访问的独立显存数量和 CPU-GPU 间的带宽不断增加,以致立即模式一次搬运少量 vertex 信息节省显存和带宽的优点逐渐失去意义,反而突显了性能方面和 driver 复杂度方面的劣势。用 shader 取代 fixed-function pipeline 是对应用程序开发者暴露更多的硬件灵活性。而且,在立即模式下,shader 只能通过预定义的 built-in variable 访问 CPU-side client 提供的 vertex 数据,去掉立即模式让 CPU 上的 client 代码和 GPU 上 shader 能直接传送任意命名的数据,二者的衔接更加直观。

OpenGL 是显卡的 C 格式机器码。有意思的是,当硬件处于初级阶段的时候,这种「机器码」还呈现一定程度的描述语言 (declaritive language) 风格。而硬件发展到高级阶段之后,反而彻底失去了高级风格。说到这儿,要回头谈谈 3D 开发的两种模式:场景 (scene) 模式和管线 (pipeline) 模式。场景模式中应用负责描述 3D 场景,由 API 负责把场景的描述翻译成屏幕显示。对开发者来说场景模式固然更容易理解,但是在《OpenGL 随想(四):计算机如何解决问题》里解释过,现有的计算能力很难完全满足场景模式。光照、阴影、平滑曲面、镜面反射等等都很难离开程序员的干预纯粹通过场景描述来自动完成。为每种场景效果都设计一个可配置的属性也让 API 过于繁琐。所以场景模式大都存在于 AutoCAD、3DS Max 这样的交互式建模工具中。而编程 API 往往采用更底层的管线模式,即在程序员理解硬件功能的基础上为硬件的计算提供必要的数据、参数和代码 (shader) 。

在 OpenGL 2.0 之前,由于硬件和 driver 只能提供固定功能的 pipeline,所以调用这些功能的方式向最常用的场景描述信息靠拢。用立即模式编写的代码,除去一些 OpenGL 状态机的状态变换,很像场景描述。所以初学 OpenGL 容易误认为这是一个简单的场景模式 API。只有接触到反射、阴影等等复杂用例之后才会转换思维。

从 OpenGL 3.0 之后,整条图形处理 pipeline 都由程序员掌握(除了少数功能,比如从 geometry shader 到 fragment shader 的光栅化)。在《OpenGL 随想(四):计算机如何解决问题》里提到过,像曲面平滑这类效果是先计算关键点在投影平面上(也就是 viewport)的颜色,然后对 viewport 中其它像素的颜色进行二维插值 (interpolation) 来完成的。在 3.2 之后,程序员可以做更灵活的选择,比如可以对每一点的法向量 (normal) 和光源向量进行插值后分别计算各点颜色,而非基于关键点的颜色插值。程序员编写的代码时会更多地从控制 pipeline 而非「描述场景」的角度思考。OpenGL 3.2 成为主流之后,初学 OpenGL 的曲线可能会更陡 (我的第一个 3.2 程序调试了四天时间。因为当整条 pipeline 的功能完全暴露在程序员面前的时候,驾驭起来并不容易。即使是一个小小的 hello world 也可能出很多错误),不过以后接触复杂用例的时候不用再经历思维模式的转换。

越灵活的功能必然呈现越底层的界面。而越高级的抽象只能暴露越僵化的功能。三年前 Ars Technica 访谈 3D FPS 引擎 Unreal 的设计师 Tim Sweeney,后者预测不光 fixed-function pipeline 会被可编程 pipeline 取代,甚至 pipeline 概念本身也会消失,3D 开发从基于 API 的方式专为使用通用编程语言。

三年后的今天,3D 开发仍旧以 OpenGL 4.2/DirectX 11 这样的 API 为最前沿,而 OS X 和 Windows 7 这样的主流环境处于 OpenGL 3.2 的水平。也许这就如同 CPU 上的编程语言一样。我不是要讨论汇编语言,这里不讨论编程语言对硬件能力的驾驭,而是说对问题描述的表现力。人们曾经用 COBOL 尝试模仿自然语言编程,就像最初的看似场景模式的 fixed-function pipeline 和立即模式,最终证明那实际上只是针对某些模式问题的肤浅方案。于是又有人像 Tim Sweeney 展望 3D 开发那样想像最灵活最有力的表达方式,得到几乎没有语法的 Lisp 语言。最终我们在一定程度上接受了动态类型、lexical closure 这样的概念,但还是在 C、Objective-C、Python、Lua 这样的中间点安顿下来。同样,在很长一段时间内 3D 开发还是会保持 API/Shading Language 的可编程 pipeline 模式。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s


%d 博主赞过: