3.WebGL:WebGL 是针对Canvas 的3D 上下文。与其他Web 技术不同,WebGL 并不是W3C 制定的标准,而是由Khronos Group 制定的。其官方网站是这样介绍的:“Khronos Group 是一个非盈利的由会员资助的协会,专注于为并行计算以及各种平台和设备上的图形及动态媒体制定无版税的开放标准。” KhronosGroup 也设计了其他图形处理API,比如OpenGL ES 2.0。浏览器中使用的WebGL 就是基于OpenGL ES2.0 制定的。
OpenGL 等3D 图形语言是非常复杂的,本书不可能介绍其中每一个概念。熟悉OpenGL ES 2.0 的读者可能会觉得WebGL 更好理解一些,因为好多概念是相通的。
本节将适当地介绍OpenGL ES 2.0 的一些概念,尽力解释其中的某些部分在WebGL 中的实现。要全面了解OpenGL,请访问www.opengl.org。要全面学习WebGL,请参考www.learningwebgl.com,其中包含非常棒的系列教程。
- 类型化数组:WebGL 涉及的复杂计算需要提前知道数值的精度,而标准的JavaScript 数值无法满足需要。为此,WebGL 引入了一个概念,叫类型化数组(typed arrays)。类型化数组也是数组,只不过其元素被设置为特定类型的值。
类型化数组的核心就是一个名为ArrayBuffer 的类型。每个ArrayBuffer 对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer 所能做的,就是为了将来使用而分配一定数量的字节。例如,下面这行代码会在内存中分配20B。
var buffer = new ArrayBuffer(20);
创建了ArrayBuffer 对象后,能够通过该对象获得的信息只有它包含的字节数,方法是访问其byteLength 属性:
var bytes = buffer.byteLength;
虽然ArrayBuffer 对象本身没有多少可说的,但对WebGL 而言,使用它是极其重要的。而且,在涉及视图的时候,你才会发现它原来还是很有意思的。
1. 视图:使用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 对象会把字节偏移量以及字节长度信息分别保存在byteOffset 和byteLength 属性中。
alert(view.byteOffset);
alert(view.byteLength);
通过这两个属性可以在以后方便地了解视图的状态。另外,通过其buffer 属性也可以取得数组缓冲器。
读取和写入DataView 的时候,要根据实际操作的数据类型,选择相应的getter 和setter 方法。下表列出了DataView 支持的数据类型以及相应的读写方法。
数据类型 | getter | setter |
有符号8位整数 | getInt8(byteOffset) | setInt8(byteOffset, value) |
无符号8位整数 | getUint8(byteOffset) | setUint8(byteOffset, value) |
有符号16位整数 | getInt16(byteOffset,littleEndian) | setInt16(byteOffset,value,littleEndian) |
无符号16位整数 | getUint16(byteOffset,littleEndian) | setUint16(byteOffset,value,littleEndian) |
有符号32位整数 | getInt32(byteOffset,littleEndian) | setInt32(byteOffset,value,littleEndian) |
无符号32位整数 | getUint32(byteOffset,littleEndian) | setUint32(byteOffset,value,littleEndian) |
32位浮点数 | getFloat32(byteOffset,littleEndian) | setFloat32(byteOffset,value,littleEndian) |
64位浮点数 | getFloat64(byteOffset,littleEndian) | setFloat64(byteOffset,value,littleEndian) |
所有这些方法的第一个参数都是一个字节偏移量,表示要从哪个字节开始读取或写入。不要忘了,要保存有些数据类型的数据,可能需要不止1B。比如,无符号8 位整数要用1B,而32 位浮点数则要用4B。使用DataView,就需要你自己来管理这些细节,即要明确知道自己的数据需要多少字节,并选择正确的读写方法。例如:
var buffer = new ArrayBuffer(20),
view = new DataView(buffer),
value;
view.setUint16(0, 25);
view.setUint16(2, 50); //不能从字节1 开始,因为16 位整数要用2B
value = view.getUint16(0);
以上代码把两个无符号16 位整数保存到了数组缓冲器中。因为每个16 位整数要用2B,所以保存第一个数的字节偏移量为0,而保存第二个数的字节偏移量为2。
用于读写16 位或更大数值的方法都有一个可选的参数littleEndian。这个参数是一个布尔值,表示读写数值时是否采用小端字节序(即将数据的最低有效位保存在低内存地址中),而不是大端字节序(即将数据的最低有效位保存在高内存地址中)