本章主要内容:
- 什么是webGL
- webGL的起源
- webGL的工作流程
1.什么是webGL
WebGL是一项用来在网页上绘制和渲染复杂三维图形(3D 图形),并允许用户与之进行交互的技术。 它是以 OpenGL ES 2.0为基础的。 与 OpenGL 一样, 它也提供绘制(Rendering(API))功能, 但是它应用在 HTML 和JavaScript 上下文中。 WebGL 绘制的曲面本质上是 HTML5 的画布, 后者最早是由 Apple 公司引入到 WebKit 开源浏览器引擎中的。 引入 HTML5 画布的理由是为了能够在 Dashboard 小部件(Widget)等应用程序中和 Apple Mac OS X 操作系统的 Safari 浏览器中绘制二维图形。
2.webGL的起源
在个人计算机上使用最广泛的两种三维图形渲染技术是 Direct3D 和 OpenGL。Direct3D 是微软 DirectX 技术的一部分, 是一套由微软控制的编程接口 (API),主要用 在Windows 平台; 而 OpenGL 由于其开放和免费的特性, 在多种平台上都有广泛地使用: 它可以在 Macintosh 或 Linux 系统的计算机、 智能手机、 平板电脑、 家用游戏机(如 PlayStation和 Nintendo)等各种电子设备上使用。 Windows 对 OpenGL 也提供了良好的 支持, 开发者也可以用它来代替 Direct3D。 OpenGL 最初由 SGI (Silicon Graphics Inc)开发, 并在 1992 年发布为开源标准。 多年以来, OpenGL 发展了数个版本, 并对三维图形开发、 软件产品开发, 甚至电影制作产生了深远的影响。 写这本书时, OpenGL桌面版的最新版本为 4.3。 虽然 WebGL根植 于 OpenGL,但它实际上是从 OpenGL 的一个特殊版本 OpenGL ES 中派生出来的, 后者专用于嵌入式计算机、 智能手机、 家用游戏机等设备。 OpenGL ES 于 2003 ~ 2004 年被 首次提出, 并在2007 年(ES 2.0)和 2012 年(ES 3.0)进行了两次升级, WebGL 是基 于 OpenGL ES 2.0 的。 这几年, 釆用 OpenGL ES 技术的电子设备的数量大幅增长, 如智能手机(iPhone 和安卓)、 平板电脑、 游戏机等。 OpenGL ES 成功被这些设备采用的部分原因是, 它在添加新特性的同时从 OpenGL 中移除了许多陈旧无用的旧特性, 这使它在保持轻量级的同时, 仍具有足够的能力来渲染出精美的三维图形。 图1.1 显示了 OpenGL、 OpenGL ES 1.1/2.0/3.0 和 WebGL 的关系。 由于 OpenGL 本身已经从 1.5 发展到了 2.0,再到 4.3,所以 OpenGL ES 被标准化为特定版本 OpenGL (OpenGL1.5 和 OpenGL2.0)的子集。
图 1.1 OpenGL. OpenGL ES 1.1//2.0/3.0 和 WebGL 之间的关系
如图 1.1 所示, 从 2.0 版本开始, OpenGL 支持了一项非常重要的特性, 即可编程着色器方法(programmable shader functions)。该特性被 OpenGL ES 2.0 继承, 并成为了 WebGL 1.0标准的核心部分。着色器方法, 或称着色器, 使用一种类似于 C 的编程语言实现了精美的视觉效果。 本书将一步步阐述着色器, 帮助你快速掌握 WebGL。 编写着色器的语言又称为着色器语言(shading language), OpenGL ES 2.0 基于。 penGL 着色器语言(GLSL),因此后者又被称为OpenGL ES 着色器语言(GLSL ES)。 WebGL 基于 OpenGL ES 2.0,也使用 GLSL ES 编写着色器。
3.webGL的工作流程
webGL工作流程主要分成下面几个阶段,其中对于WebGL 程序员来说, 最重要的是顶点着色器和片段
着色器。
顶点着色器
为了得到一个真实感的3D场景, 仅仅绘制某些位置的对象是不够的, 还需要考虑到灯光照射到这些对象时的效果。 我们用一个通用的术语表示确定灯光对不同材质效果的整个过程, 即着色(shading)。 在 WebGL 中, 着色过程分为以下两个阶段:
- 顶点着色器
- 片段着色器
第一阶段是顶点着色器(片段着色器是流水线的后阶段,会后面介绍)。 顶点着色器这个名称来源是这样的事实; 我们总是用顶点(vertex)表示几何对象的角点或交点。 顶点着色器是流水线中对顶点进行着色的阶段。 下图是顶点着色器在 WebGL图形流 水线中的作用。
顶点着色器是 3D 建模数据(即顶点)经过 JavaScript API 后首先到达的地方。 顶点着色器是可编程的, 即用户可以自己设计顶点着色器的源代码, 并且可以用 JavaScript API 传入 源代码, 因此实际上顶点的处理有很多不同的方法。 在着色过程实际开始之前, 着色器通常先对顶点进行转变, 即乘以一个变换矩阵。 把一个对象的全部顶点都乘以一个变换矩阵, 其实际作用相当于把每个对象放置在场景中的某个位置。 顶点着色器的输入包括以下内容:
- attribute变量(属性变量) 。 它们是一些用户自定义的变量, 它们通常用来包含特定于每个顶点的数据(也有一个名为常量顶点属性(constant vertex attribute)的功能, 当我们需要为多个顶点定义同一个属性值时, 可以使用这个功能)。顶点位置和顶点 颜色就是顶点属性(attribute 变量) 的示例。
- uniform变量(恒值变量) 。 它们用来表示所有顶点都相同的数据。 变换矩阵和光源位置都属于uniform 变量(正如本章后面将指出, 在两次 WebGL 绘制调用之间, 可以改变 unifbrm 变量的值。 因此, 它们只是在一次绘制调用期间保持不变)。
- 顶点着色器的实际源代码。这些源代码是用OpenGLES着色语言(OpenGL ES Shading Language, GL SL ES)设计的。
- varying 变量(易变变量) 是顶点着色器向片段着色器发送信息的一个手段。
- 内置变量 gl_Position 是最重要 的一个变量,而且即使在顶点着色器完成处理之后, 它还保存着顶点的位置信息。
下图表示了顶点着色器的输出, 它包括用户自定义的 varying 变量、 一些内置的特殊变量。
图元装配
在顶点着色器之后是图元装配(primitive assembly)。在这一步操作中, WebGL流水线需要把已经着色的顶点装配成三角形、 线段或点精灵(point sprites)等几何图元。 然后 对每个三角形、线段或点精灵, WebGL 需要判断它们在当前时刻是否位于屏幕上可见的 3D 区域中。
光栅化
流水线的下一个阶段是把图元(线段、三角形和点精灵)转换为片段, 然后把片段传送给片段着色器。 我们可以把片段看成最终绘制在屏幕的一个像素。 这个转换过程岀现在光栅化操作。
片段着色器
来自光栅化的片段需要发送到流水线的第二个可编程阶段, 即片段着色器。 正如前面曾提到, 一个片段实际上对应于屏幕上的一个像素。 但是,并非所有的片段都会成为绘制缓存中的像素, 因为逐片段操作可能会在流水线的最后几个步骤中丢弃某些片段。 因此,WebGL 需要区分片段和像素, 把最终能够写入到绘制缓存中的片段称为像素。
片段着色器的输入数据包括以下内容:
- 用 OpenGL ES 着色语言表示的片段着色器的源代码;
- 一些内置的特殊变量(如 gl_PointCoord):
- 用户自定义的 varying 变量, 它们的值由顶点着色器写入;
- uniform 变量, 它们是一些特殊变量, 它们的值对所有片段都是恒量;
- 釆样器(Sampler),它们是一类特殊的 uniform 变量, 用于纹理映射。
正如前面在讨论顶点着色器时曾指出, 通过varying变量把顶点着色器中的信息传送给片段着 色器。 然而 通常片段数多于顶点数, 写入到顶点着色器的 varying变量的值是对片段进行线性插值得到的。 片段着色器需要读取的 varying变量值 是由线性插值得到的,它们不同于写入到顶点着色器中的值。
差值计算:
在片段着色器之后, 都要把每个片段传送到流水线的下一个阶段, 此阶段包含逐片段操作。 顾名思义, 这步操作实际上包含几个子操作。 来自片段着色器的每个片段都可以以不同方式影响 绘制缓存中的一个像素, 具体取决于逐片段操作的条件和结果。
- 裁剪测试
裁剪测试决定片段是否位于裁剪矩形中。 此裁剪矩形是由一个左下角的坐标、一个宽度和一个高度决定的。 如果片段位于裁剪矩形内,则通过测试,并把片段传递给下一个阶段。 如果片段位于裁剪矩形之外, 则被丢弃,而且不会到达绘制缓存。
- 多重釆样片段操作
这一步操作修改片段的 alpha 值和覆盖值(coverage),作为抗锯齿的一个措施。 在计算机图形学中, 抗锯齿技术是指这样的技术:它用来改善多边形线框的外观使得它们看起来没有锯齿一一即在屏幕得到光滑的结果。
- 深度缓存测试
深度缓存测试根据深度缓存(也称 Z-缓存)的值丢弃输入的部分片段。 当我们把一个 3D 场景绘制到 2D 的颜色缓存中时, 颜色缓存只存储场景中在某个时刻对观察者可见对象的颜色。 有些对象可能会被其他对象遮挡, 深度缓存和深度缓存测试决定哪些像素出现在颜色缓存中。 对于每个像素, 深度缓存都要保存观察者到当前最近图元的距离。 对于传入的片段, 把它的 z 值与深度缓存中同一个位置上的 z 值进行比较。 如果传入 片段的z 值比较小, 则表示新传入的片段比颜色缓存中的原来像素离观察者更近, 则传入 的片段通过测试。如果传入片段的 z 值大于 Z-缓存中相应位置的值, 则表示新片段被绘制 缓存中的像素遮挡, 因此丢弃这个新传入的片段。
- 融合
流水线中的下一个操作是融合(blending)。利用融合, 把传入片段的颜色与已经在颜色缓存中相应位置的片段的颜色进行组合。当我们要创建透明对象时,就需要使用融合技术。
- 抖动
位于绘制缓存之前的最后一个步骤是抖动(dithering)。 颜色缓存用有限的二进制位数表示每个颜色。抖动用来以某种方式排列颜色,得到比实际颜色数更多的颜色。当颜色缓存可用的颜色数有限时,抖动就非常有用。