WebGL

WebGL 是针对 Canvas 的 3D 上下文。与其他 Web 技术不同,WebGL 并不是 W3C 制定的标准,而
是由 Khronos Group 制定的。其官方网站是这样介绍的:“Khronos Group 是一个非盈利的由会员资助的
协会,专注于为并行计算以及各种平台和设备上的图形及动态媒体制定无版税的开放标准。” Khronos
Group 也设计了其他图形处理 API,比如 OpenGL ES 2.0。浏览器中使用的 WebGL 就是基于 OpenGL ES
2.0 制定的。

WebGL学习地址,
OpenGL

类型化数组

WebGL 涉及的复杂计算需要提前知道数值的精度,而标准的 JavaScript 数值无法满足需要。为此,WebGL 引入了一个概念,叫类型化数组(typed arrays)。类型化数组也是数组,只不过其元素被设置为特定类型的值。
类型化数组的核心就是一个名为 ArrayBuffer 的类型。每个 ArrayBuffer 对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过 ArrayBuffer 所能做的,就是为了将来使用而分配一定数量的字节。
视图
使用 ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。其中,最常见的视图是 DataView,通过它可以选择 ArrayBuffer中一小段字节。为此,可以在创建 DataView实例的时候传入一个 ArrayBuffer、一个可选的字节偏移量(从该字节开始选择)和一个可选的要选
择的字节数。

示例:

//基于整个缓冲器创建一个新视图
var view = new DataView(buffer);
//创建一个开始于字节 9 的新视图
var view = new DataView(buffer, 9);
//创建一个从字节 9 开始到字节 18 的新视图
var view = new DataView(buffer, 9, 10); 
类型化视图
类型化视图一般也被称为类型化数组,因为它们除了元素必须是某种特定的数据类型外,与常规的
数组无异。类型化视图也分几种,而且它们都继承了 DataView。
  • Int8Array:表示 8 位二补整数。
  • Uint8Array:表示 8 位无符号整数。
  • Int16Array:表示 16 位二补整数。
  • Uint16Array:表示 16 位无符号整数。
  • Int32Array:表示 32 位二补整数。
  • Uint32Array:表示 32 位无符号整数。
  • Float32Array:表示 32 位 IEEE 浮点值。
  • Float64Array:表示 64 位 IEEE 浮点值。

WebGL上下文

目前,在支持的浏览器中,WebGL 的名字叫"experimental-webgl",这是因为 WebGL 规范仍然未制定完成。制定完成后,这个上下文的名字就会变成简单的"webgl"。如果浏览器不支持 WebGL,那么取得该上下文时会返回 null。

示例:

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
 var gl = drawing.getContext("experimental-webgl");
 if (gl){
 //使用 WebGL
 }
} 
通过给 getContext()传递第二个参数,可以为 WebGL 上下文设置一些选项。
  • alpha:值为 true,表示为上下文创建一个 Alpha 通道缓冲区;默认值为 true。
  • depth:值为 true,表示可以使用 16 位深缓冲区;默认值为 true。
  • stencil:值为 true,表示可以使用 8 位模板缓冲区;默认值为 false。
  • antialias:值为 true,表示将使用默认机制执行抗锯齿操作;默认值为 true。
  • premultipliedAlpha:值为 true,表示绘图缓冲区有预乘 Alpha 值;默认值为 true。
  • preserveDrawingBuffer:值为 true,表示在绘图完成后保留绘图缓冲区;默认值为 false。

示例:

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if (drawing.getContext){
 try {
 gl = drawing.getContext("experimental-webgl");
 } catch (ex) {
 //什么也不做
 }
 if (gl){
 //使用 WebGL
 } else {
 alert("WebGL context could not be created.");
 }
} 

常量

如果你熟悉 OpenGL,那肯定会对各种操作中使用非常多的常量印象深刻。这些常量在 OpenGL 中
都带前缀 GL_。 在 WebGL 中,保存在上下文对象中的这些常量都没有 GL_前缀。比如说,
GL_COLOR_BUFFER_BIT 常量在 WebGL 上下文中就是 gl.COLOR_BUFFER_BIT。WebGL 以这种方式支
持大多数 OpenGL 常量(有一部分常量是不支持的)。

方法命名

OpenGL(以及 WebGL)中的很多方法都试图通过名字传达有关数据类型的信息。如果某方法可以
接收不同类型及不同数量的参数,看方法名的后缀就可以知道。方法名的后缀会包含参数个数(1 到 4)
和接收的数据类型(f 表示浮点数,i 表示整数)。例如,gl.uniform4f()意味着要接收 4 个浮点数,
而 gl.uniform3i()则表示要接收 3 个整数。
也有很多方法接收数组参数而非一个个单独的参数。这样的方法其名字中会包含字母 v(即 vector,
矢量)。因此,gl.uniform3iv()可以接收一个包含 3 个值的整数数组。请

准备绘图

在实际操作 WebGL 上下文之前,一般都要使用某种实色清除,为绘图做好准备。为此,
首先必须使用 clearColor()方法来指定要使用的颜色值,该方法接收 4 个参数:红、绿、蓝和透明度。
每个参数必须是一个 0 到 1 之间的数值,表示每种分量在最终颜色中的强度。
gl.clearColor(0,0,0,1); //black
gl.clear(gl.COLOR_BUFFER_BIT);

以上代码把清理颜色缓冲区的值设置为黑色,然后调用了 clear()方法,这个方法与 OpenGL 中的
glClear()等价。传入的参数 gl.COLOR_BUFFER_BIT 告诉 WebGL 使用之前定义的颜色来填充相应区
域。一般来说,都要先清理缓冲区,然后再执行其他绘图操作。

视口与坐标

开始绘图之前,通常要先定义 WebGL 的视口(viewport)。默认情况下,视口可以使用整个
区域。要改变视口大小,可以调用 viewport()方法并传入 4 个参数:(视口相对于元素的)
x 坐标、y 坐标、宽度和高度。

缓冲区

顶点信息保存在 JavaScript 的类型化数组中,使用之前必须转换到 WebGL 的缓冲区。要创建缓冲区,可以调用 gl.createBuffer(),然后使用 gl.bindBuffer()绑定到 WebGL 上下文。这两步做完之
后,就可以用数据来填充缓冲区了。

调用 gl.bindBuffer()可以将 buffer 设置为上下文的当前缓冲区。此后,所有缓冲区操作都
直接在 buffer 中执行。因此,调用 gl.bufferData()时不需要明确传入 buffer 也没有问题。最后
一行代码使用 Float32Array 中的数据初始化了 buffer(一般都是用 Float32Array 来保存顶点信
息)。如果想使用 drawElements()输出缓冲区的内容,也可以传入 gl.ELEMENT_ARRAY_BUFFER。
gl.bufferData()的最后一个参数用于指定使用缓冲区的方式,取值范围是如下几个常量。

  • gl.STATIC_DRAW:数据只加载一次,在多次绘图中使用。
  • gl.STREAM_DRAW:数据只加载一次,在几次绘图中使用。
  • gl.DYNAMIC_DRAW:数据动态改变,在多次绘图中使用。
  • gl.deleteBuffer(buffer):删除缓存,释放内存

错误

JavaScript 与 WebGL 之间的一个最大的区别在于,WebGL 操作一般不会抛出错误。为了知道是否
有错误发生,必须在调用某个可能出错的方法后,手工调用 gl.getError()方法。这个方法返回一个
表示错误类型的常量。可能的错误常量如下。
  • gl.NO_ERROR:上一次操作没有发生错误(值为 0)。
  • gl.INVALID_ENUM:应该给方法传入 WebGL 常量,但却传错了参数。
  • gl.INVALID_VALUE:在需要无符号数的地方传入了负值。
  • gl.INVALID_OPERATION:在当前状态下不能完成操作。
  • gl.OUT_OF_MEMORY:没有足够的内存完成操作。
  • gl.CONTEXT_LOST_WEBGL:由于外部事件(如设备断电)干扰丢失了当前 WebGL 上下文。

着色器

着色器(shader)是 OpenGL 中的另一个概念。WebGL 中有两种着色器:顶点着色器和片段(或像
素)着色器。顶点着色器用于将 3D 顶点转换为需要渲染的 2D 点。片段着色器用于准确计算要绘制的每个像素的颜色。WebGL 着色器的独特之处也是其难点在于,它们并不是用 JavaScript 写的。这些着色
器是使用 GLSL(OpenGL Shading Language,OpenGL 着色语言)写的,GLSL 是一种与 C 和 JavaScript
完全不同的语言。

编写着色器

GLSL 是一种类 C 语言,专门用于编写 OpenGL 着色器。因为 WebGL 是 OpenGL ES 2.0 的实现,所
以 OpenGL 中使用的着色器可以直接在 WebGL 中使用。这样就方便了将桌面图形应用移植到浏览器中。
每个着色器都有一个 main()方法,该方法在绘图期间会重复执行。为着色器传递数据的方式有两
种:Attribute 和 Uniform。通过 Attribute 可以向顶点着色器中传入顶点信息,通过 Uniform 可以向任何
着色器传入常量值。Attribute 和 Uniform 在 main()方法外部定义,分别使用关键字 attribute 和
uniform。

编写着色器程序

浏览器不能理解 GLSL 程序,因此必须准备好字符串形式的 GLSL 程序,以便编译并链接到着色器
程序。为便于使用,通常是把着色器包含在页面的