简介


官方网站:OpenGL ES

OpenGL ES,全称为 Open Graphics Library for Embedded Systems,作为 OpenGL 的移动设备版本,其最主要的特点是精简。

OpenGL 是一个计算机图形接口标准,脱胎于 SGI 公司的 IRIS GL。2000年由 Khronos Group 接手。OpenGL 在设计上最大的特点是跨平台,为了达到这个目的,设计出来的接口便与硬件无关,但是这种设计也是有代价的,就是它不提供与窗口、音频、键盘、鼠标都输入输出设备的接口。

在设计上,OpenGL 采用了 Client-Server 的设计模式。应用程序作为 Client,调用绘图接口,并发送给 Server,具体的绘制计算在 Server 完成,最终生成一张二维图形供 Client 显示。因此,OpenGL 通常是跨网络的。这一点与 X-Window 的设计是一致的。

图片出自Apple

既然是标准,那么就说明它只是一纸规范,具体的实现有赖于硬件产商。好在 Intel、Nvidia、AMD 这些奸商都已经在支持 OpenGL 的商家列表名单上,所以市面上贩售的大部分显卡都是支持 OpenGL 的。

竞争对手

微软出品的 DirectX 事实上是 Windows 系统上的行业标准,它只支持 Windows,所以更加专注,能提供与窗口、输入输出设备交互的接口。已经成为 OpenGL 最大的竞争对手。

专一的设计虽然被开放社区所不耻,但这种设计虽牺牲了跨平台特性,却赢得了更加丰富的单平台支援,往往也可以优化得最好。

与OS的关系示意图

图片出处

请将上图的 Mesa 3D 自行脑补成 OpenGL,将 Linux Kernel 脑补成各种 OS,原理一致。

谁在使用 OPENGL?


不可避免的,如果有牛X哄哄的软件在使用 OpenGL,将会使其身价大增。Wiki 上有一个列表,估计也是不完整的,但已经很说明问题了:

这里随便举几个吧:

  • Adobe Photoshop CS3/CS4
  • Autodesk CAD
  • Autodesk Maya
  • 3DMax
  • Google Earth
  • Unity3d
  • Cocos2d

但也请注意一点,有时候某个软件或者技术的流行,并不是因为其出色,而是人们实在别无选择。

发布时间


2004.9:OpenGL 2.0 发布。

2007.3:OpenGL ES 2.0 发布。

2008.8:OpenGL 3.0 发布。

2010.3:OpenGL 4.0 发布。

2012.8:OpenGL ES 3.0 发布。

从 iPhone 3GS 之后,开始支持 OpenGL ES 2.0,前几代包括 iPhone 3G 都不支持 2.0。可以肯定的一点就是,现在市面上贩售的应该都已经支持 2.0 了。

OPENGL ES 2.0


OpenGL ES 目前已经发展到 4.x。我的笔记只学习 2.x,这个版本相对于它的前一版,也就是 1.x,有个最重要的变化,就是被称为「Programmable Pipeline」的引入。

Programmable Pipeline

pipeline 被翻译成「管线」,采用的是直译的方法,其更加确切的意思是「流水线」。整个图形绘制的流程,就像是工厂车间的流水线一样,需要一道道的工序连接组成,上一道工序的输出作为下一道工序的输入。因此,整个过程被称为「pipeline」。

在 1.x 阶段,整条流水线是固定的,但每道工序都提供了一些开关选项来控制输出。到了 2.0 阶段,流水线上的两道工序,变成可编程的了,也就是工人们可以根据需求自定义工序。

于是,工人们唱着歌谣,「2.0大法好,信2.0得永生」。但是,工人们忘了,狗屁的委员会是在微软的竞争压力下才想起来改进流水线的。DirectX工厂的工人们早就享受到新的着色语言了,虽然 OpenGL 历史比 DirectX 早,但那时工人们却唱道,「出道早算个屁,工人们没福利」。OpenGL 总算出了 2.0 版本,来应对 DirectX 的挑战。

OpenGL ES 1.x 与 2.0 流水线区别

原图出处

由上面两张图的对比,可以看到,变化仅仅在两道工序上:

  • Transform and Lighting 变成了 Vertex Shader
  • Texture Environment、Color Sum、Fog、Alpha Test 变成了 Fragment Shader

因此,Vertex Shader 和 Fragment Shader 是 2.0 的核心内容。这两个术语分别被翻译成「顶点着色器」和「片元着色器」。

什么是片元?图片之元也。也就是组成图形的最基本元素。事实上,每个fragment对应屏幕上的一个像素,只不过还附带了一些属性。

OPENGL ES 2.0 流水线


这张图就是流水线。信息量颇大,下面逐一解释。注意,图上的每张方块图都表示流水线上的一道工序,既云工序,就有输入和输出。

Vertex Shader

顶点着色器。它是可编程的,用来操作顶点。

OpenGL的基本图元是点、线、三角形,所有其他图形都由这三种基本图元组成。这三种图元又都是由顶点组成。譬如要绘制一个三角形,需要三个顶点,以此类推。

输入:

  • Attributes:每个顶点的属性。譬如坐标、颜色、法线(用于处理光照)、纹理坐标。
  • Uniforms:顶点着色器使用的一些常量。
  • Samplers:用以表示纹理的特殊的 uniforms。
  • Shader program:着色器的代码。

输出:

  • Varying variables:这些变量被用在「图元光栅化」(Primitive Rasterization)阶段,并作为「Fragment Shader」的输入。
  • gl_Position:顶点位置。
  • gl_PointSize:点的大小。
  • gl_FontFacing:布尔值,表示片元是否属于一个font-facing图元。

      在官方的 Reference Card 文档中,Vertex Shader Outputs 只有 gl_Position 和 gl_PointSize,没有 gl_FontFacing。
    

顶点着色器代码示例:

Primitive Assembly

图元装配。

「primitive」意为「原始的、原子的、基元」,此处意为「图形的基本元素」(即点、线、三角形),因此译成「图元」。

「assembly」意为「装配、组装」,犹如 iPhone 背面刻着「Assembled in China」(在中国组装)。

图元是一个可以绘制的几何对象。这句话很抽象,我换句表述吧,图元就是点、线、三角形这三种 OpenGL ES 可以直接绘制的几何对象。图元就是这三种。

在上一个阶段,我们处理的对象是顶点,而在「装配」阶段,我们需要将这些顶点「装配」成点、线、三角形这三种几何图元。装配阶段,包含了「裁剪」(clipping)操作,譬如图元如果在可视区域之外,将被丢弃。还有一种称为「剔除」(culling)的操作,处理的是要剔除正面还是反面的问题。

输入:顶点

输出:几何图元对象,点、线、三角形。

在经过「clipping」和「culling」之后,就进入下一道工序「光栅化」。

Rasterization

光栅化。

光栅化就是将几何图元(primitive)转换成二维的像素片元(fragment)的过程。上一步我们得到的是几何图元,最终需要将其显示在像素显示器上,因此需要将其与屏幕的像素格子对应起来。

譬如我们画一个三角形,最初只是指定了三个顶点的位置,最终画出来的不止是这三个顶点,而是由三个顶点围成的一个图形,包括了一片的区域、一组像素点。这就是「光栅化」的功劳。它确定了由这三个输入的顶点围成的几何区域,所包含的那些像素点的集合,所有这些点最终都将被渲染。

每个片元(fragment)都对应屏幕上的一个像素,且带有颜色等属性。

输入:几何图元对象

输出:二维的片元

Fragment Shader

片元着色器。它是可编程的,用以操作片元。

输入:

  • Varying varivables:顶点着色器的输出,光栅化后的计算结果。
  • Uniforms:片元着色器使用的一些常量。
  • Samplers:用以表示纹理的特殊的 uniforms。
  • Shader program:着色器代码。

可以看到,跟 Vertex Shader 一模一样的。

输出:

  • gl_FragColor:片元的颜色。
  • gl_FragData[n]:fragment color for color attachment n

      gl_FragData[n]从官方 Reference Card 文档得知,上图并无。
    

片元着色器示例代码:

Per-Fragment Operations

所谓 Per-Fragment Operations 就是对每个 fragment 进行的一系列工序,由于每个 fragment 对应屏幕上的一个像素,因此,这个操作量是很大的,关系到整条流水线的速度问题。如果这道工序做的好,流水线的效率就高,反之亦然。

工序包括:

  • Pixel ownership test:像素归属测试。测试像素是否属于当前的上下文,譬如当前的窗口被遮挡,那么被遮挡的部分,其像素就可以丢弃不画。
  • Scissor test:裁剪测试。如果片元不在裁剪矩形之内,则丢弃。
  • Stencil and depth test:模板和深度测试。根据此两项传入的值判定是否要丢弃。
  • Blending:混合。将新计算所得之片元颜色值与存储在framebuffer对应位置的颜色值混合。
  • Dithering:抖动。最小化精度误差。

输入:片元

输出:framebuffer

FrameBuffer

帧缓冲区。对应着最终要画到屏幕上的二维图形。

小结


整个流水线大概就是这个样子。具体的需要配合实例加以说明,才能更加深入理解。

一些概念的整理:

OpenGL

Open Graphics Library 的缩写,意为开放图形库。定义了一套跨平台的3D图形绘制标准,目前由Khronos Group管理,旗下有各大奸商加盟,例如 Intel、NVIDIA、QUALCOMM、ARM、SCE、Apple、Google等。

OpenGL ES 是其为嵌入式系统做的版本,主要是精简了OpenGL的接口,以期适应各种配置低端的移动设备。

Pipeline

流水线,或译成「管线」(这个翻译不怎么样,简直就是强行翻译),还是流水线较为贴切。为何?「管线」令人想起的是传输气体、液体的管道线,它跟 OpenGL 的渲染过程实在是一点都不像。OpenGL 的渲染过程是「流水线」式的。

因为整个图形的绘制过程,被设计成像工厂车间的流水线一样,经过一道道处理(即工序),最终在屏幕上画出图形,因此称为「Pipeline」。

Vertex

顶点。是几何图形的组成最基本部分。

Fragment

片元。其对应一个像素点,且包含该像素点的一组属性,譬如颜色值。

Shader

着色器。顾名思义,最终目的是「着色」。OpenGL ES 有两种着色器,Vertex Shader 和 Fragment Shader。因其处理对象分别是 Vertex(顶点)和 Fragment(片元)而得名。

Primitive

图元。有别于「片元」,「片元」是像素意义上的,而「图元」则是几何意义上的。OpenGL ES 中,共有三种图元,分别是:点、线、三角形。这代表了三种几何图形,其他复杂图形都由这三种组成。「图元」又是有一系列的「顶点」来组成,每个顶点都包含了坐标、颜色等属性。

Rasterization

光栅化。就是将几何图形转换成像素点的集合的过程。亦即从「图元」到「片元」的过程。