本章主要内容:
- 三角形在三维图形学中的重要地位
- 使用多个三角形绘制其他类型的基本图形
- 利用简单的方程对三角形做基本的变换,如移动,旋转和缩放
- 利用矩阵简化变换
4.1 绘制多个点
不管三维模型的形状多么的复杂,其基本组成部分就是三角形,只不过复杂的模型是由更多的三角形构成而已。
为了绘制多个点,webgl提供了一种很方便的机制,既缓冲区对象,它可以一次性的向着色器中传入多个顶点数据。缓冲区对象是webgl系统中的一块内存区域,我们可以一次性的向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。
流程如下:
- 获取webgl绘图的上下文
- 初始化着色器
- 设置点的坐标信息
- 设置canvas的背景颜色
- 清空canvas
- 绘制
html代码如下:
1 |
|
js代码
1 | // 顶点着色器 注意这里的\n不能省略。否则不符合语法 |
4.2 使用缓存区对象
缓存对象是webgl系统中的一块存储区,你可以在缓冲区对象中保存想要绘制的所有的顶点数据。如图,先创建一个缓冲区对象,然后向其中写入顶点数据,你就能一次性的向顶点着色器中传入多个顶点的attribute变量数据。
使用缓冲区对象向顶点着色器传入多个顶点数据,需要遵循5个步骤
- 创建缓冲区对象(gl.createBuffer())
- 绑定缓冲区对象(gl.bindBuffer())
- 将数据写入缓冲区对象(gl.bufferData())
- 将缓存区对象分配给一个attribute变量(gl.vertexAttribPointer())
- 开启attribute变量(gl.enableVertexAttribArray())
4.3 创建缓冲区对象
使用缓冲区对象之前,必须创建它
var vertexBuffer = gl.createBuffer()
如图表示的是创建缓冲区对象前后webgl系统的中间状态。上面是创建前,下面是创建后。
gl.createBuffer()
类型 | 参数 | 含义 |
---|---|---|
返回值 | 非null | 新创建的缓冲区对象 |
null | 创建缓冲区对象失败 | |
错误 | 无 |
相应的gl.deleteBuffer(buffer)
函数可以用来删除缓冲区对象
gl.deleteBuffer(buffer)
类型 | 参数 | 含义 |
---|---|---|
参数 | buffer | 待删除的缓冲区对象 |
返回值 | 无 | |
错误 | 无 |
4.4 绑定缓冲区
创建缓冲区之后的第2个步骤就是将缓冲区对象绑定在webgl系统中已经存在的“目标”上,这个“目标”表示缓冲区对象的用途。这样webgl才能够正确处理其中的内容。
gl.bindBuffer(target,buffer)
类型 | 参数 | 含义 |
---|---|---|
target | gl.ARRAY_BUFFER | 表示缓冲区对象中包含了顶点的数据 |
gl.ELEMENT_ARRYA_BUFFER | 表示缓冲区对象中包含了顶点的索引值 | |
buffer | 指定之前由gl.createBuffer() 返回的待绑定的缓冲区对象 | |
null | 禁用对target的绑定 | |
返回值 | 无 | |
错误 | INVALID_ENUM | target不是上述值之一,这时将保持原有的绑定情况不变 |
4.5 向缓冲区对象中写入数据(gl.bufferData())
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW)
该方法的效果是将第2个参数vertices中的数据写入了绑定到第1个参数gl.ARRAY_BUFFER上的缓冲区对象。我们不能直接向缓冲区对象写入数据,只能向“目标”写入数据。所以向缓冲区对象写入数据前,必须先绑定。
该方法执行后,webgl系统的内部状态如下:
gl.bufferData(target,data,usage)
类型 | 参数 | 含义 |
---|---|---|
target | gl.ARRAY_BUFFER或gl.ELEMENT_ARRAY_BUFFER | |
data | 写入缓冲区对象的数据 | |
usage | gl.STATIC_DRAW | 只会想缓冲区对象中写入一次数据,但需要绘制多次 |
gl.STREAM_DRAW | 只会向缓冲区对象中写入一次数据,然后绘制若干次 | |
gl.DYNAMIC_DRAW | 会向缓冲区对象多次写入数据,并绘制很多次 | |
返回值 | 无 | |
错误 | INVALID_ENUM | target不是上述值之一,这时将保持原有的绑定情况不变 |
4.6 类型化数组
为了绘制三维图形,webgl通常需要同时处理大量相同类型的数据,例如顶点的坐标和颜色数据。为了优化性能,webgl为每种数据类型引入了一种特殊的数组。浏览器事先知道了数组中的数据类型,所以处理起来更加有效率。
webgl中使用的各种类型话数组
数组类型 | 每个元素所占字节数 | 描述 |
---|---|---|
Int8Array | 1 | 8位整型数 |
UInt8Array | 1 | 8位无符号整型数 |
Int16Array | 2 | 16位整型数 |
UInt16Array | 2 | 16位无符号整型数 |
Int32Array | 4 | 32位整型数 |
UInt32Array | 4 | 32位无符号整型数 |
Float32Array | 4 | 单精度32位浮点数 |
Float64Array | 8 | 双精度64位浮点数 |
类型化数组的方法和属性
方法、属性和常量 | 描述 |
---|---|
get(index) | 获取第index个元素值 |
set(index,value) | 设置第index个元素值为value |
set(array,offset) | 从第offset个元素开始,将数组array中的值填充进去 |
length | 数组的长度 |
BYTES_PER_ELEMENT | 数组中每个元素所占的字节数 |
类型化数组的创建方法和普通数组一样
4.7 将缓冲区对象分配给attribute变量(gl.vertexAttribPointer)
你可以使用gl.vertexAttrib[1234]f
系列函数为attribute变量分配值。但是这些方法一次只能向attribute变量分配一个值。而现在,你需要将整个数组中的所有值一次性的分配给attribute变量。而gl.vertexAttribPointer
解决了这个问题。
gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0)
gl.vertexAttribPointer(location,size,type,normalized,stride,offset)
将gl.ARRAY_BUFFER的缓冲区对象分配给由location指定的attribute变量
类型 | 参数 | 含义 |
---|---|---|
location | 指定待分配attitude变量的存储位置 | |
size | 指定缓冲区中每个顶点分量个数(1,4),若size比attribute变量需要分配的分量数小,缺失分量将按照与gl.vertexAttrib[1234]f相同的规则进行补全。 | |
type | gl.UNSIGNED_BYTE | 无符号字节,Uint8Array |
gl.SHORT | 短整型,Int16Array | |
gl.UNSIGNED_SHORT | 无符号短整型,Uint16Array | |
gl.INT | 整型,Int32Array | |
gl.UNSIGNED_INT | 无符号整型,Uint32Array | |
gl.FLOAT | 浮点型,Float32Array | |
normalize | 传入true或者false,表明是否将非浮点型的整数归一化到[0,1]或者[-1,1]的区间 | |
stride | 指定相邻两个顶点的字节数,默认为0 | |
offset | 指定缓冲区对象中的偏移量(以字节为单位),既attribute变量从缓冲区的何处开始存储,如果是从起始位置,offset设为0 |
4.8 开启attribute变量
为了使顶点着色器能够访问缓冲区内的数据,我们需要gl.enableVertexAttribArray(a_Position)
方法来开启attribute变量。这时候缓冲区对象和attribute变量之间才能真正连接起来。
gl.enableVertexArray(location)
类型 | 参数 | 含义 |
---|---|---|
参数 | location | 指定attribute变量的存储位置 |
返回值 | 无 | |
错误 | INVALID_VALUE | location大于等于attribute变量的最大数目 |
同样你也可以关闭分配
gl.disableVertexArray(location)
类型 | 参数 | 含义 |
---|---|---|
参数 | location | 指定attribute变量的存储位置 |
返回值 | 无 | |
错误 | INVALID_VALUE | location大于等于attribute变量的最大数目 |
4.9 hello triangle
你已经学会了如何将多个顶点坐标传递给顶点着色器,下面我们使用这些顶点绘制真正的图形
html代码跟之前的一样,这里就不写了,js代码如下:
1 | // 顶点着色器 注意这里的\n不能省略。否则不符合语法 |
绘制三角形的程序跟绘制多个点的程序基本一样,只有两个地方不一样。
- 在顶点着色器中,指定顶点尺寸的
gl_PointSize = 10.0
被删去了。该语句只有在绘制单个顶点的时候才起作用 - gl.drawArrays() 方法的第一个参数从gl.POINTS被改为了gl.TRIANGLES