在实际应用中,肯定不能只绘制一个点😭,需要绘制到各种各样的图形,三角形四边形五边形等等,但是所有多边形都可以看做是由1到n个三角形△拼接而成。
1.了解gl.drawArrays
WebGL方法 gl.drawArrays ,通过第一个参数指定不同的值,可以以多种不同的方式来绘制图形。在之前的例子中,绘制点都了解过了,这边我们单独拉出来看看。对比下绘制图形与绘制点之间的区别。
gl.drawArrays(gl.POINTS,0,1)//绘制一个点
从上面的代码片段看到gl.drawArrays(mode, first, count);有三个参数,分别是mode、first、count
mode:显而易见,是模式,区别于绘制一个点(gl.POINT),还是一个三角形(gl.trangle)等等,点和三角形用的最多。下面将所有模式列举出来,需要的时候不至于陌生。
gl.POINTS: 绘制一系列点。gl.LINE_STRIP: 绘制一个线条。即,绘制一系列线段,上一点连接下一点。gl.LINE_LOOP: 绘制一个线圈。即,绘制一系列线段,上一点连接下一点,并且最后一点与第一个点相连。gl.LINES: 绘制一系列单独线段。每两个点作为端点,线段之间不连接。gl.TRIANGLE_STRIP:绘制一个三角带gl.TRIANGLE_FAN:绘制一个三角扇gl.TRIANGLES: 绘制一系列三角形。每三个点作为顶点。
first:指定从哪个点开始绘制。
count:指定绘制需要使用到多少个点。即有多少个顶点参与绘制,多余的会被忽略。
再回过来看这行代码gl.drawArrays(gl.POINTS,0,1),表明从最开始的一个点绘制,绘制一个点(多了也没有😭,意思就算有多个,这里的count写了1,也只绘制一个)。
2. 绘制一个三角形
显而易见,我们会使用gl.TRIANGLES来绘制三角形,三角形需要三个顶点信息(有点废话)。
这里我们设置三个顶点坐标A(-0.5,0.0),B(0.5,0.0),C(0.0,0.8)。
let vertices = [
-0.5, 0.0,
0.5, 0.0,
0.0, 0.8
]
vertices = new Float32Array(vertices)
采用WebGLBuffer对象来往WebGL中传递数据。这个buffer就相当于是中间人,WebGLBuffer与这个 Float32Array之间不互通,需要buffer来翻译一下。这个翻译分为5个步骤(看代码中的注释)
// 1 创建缓冲区对象
let buffer = gl.createBuffer()
// 2 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER,buffer)
// 3 向缓冲区对象中写入数据
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW)
// 4 把带有数据的buffer赋值给attribute,也就是分配给a_position变量
let a_position = gl.getAttribLocation(gl.program,'a_position')
gl.vertexAttribPointer(
a_position,
2,
gl.FLOAT,
false,
0,
0
)
// 5:确认把带有数据的buffer赋值给attribute(起连接的作用)
gl.enableVertexAttribArray(a_position)
详细讲下第四步骤中带6个变量的那行,该函数告诉显卡如何从当前绑定的缓冲区(bindBuffer() 指定的缓冲区)中读取顶点数据。
gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
- location:location: vertex Shader里面attribute变量的location
- size:指定每个顶点属性的组成数量,必须是 1,2,3 或 4。attribute变量的长度(vec2)
- type:指定数组中每个元素的数据类型:gl.BYTE,gl.SHORT,gl.UNSIGNED_BYTE,gl.UNSIGNED_SHORT,gl.FLOAT。
- normalized:当转换为浮点数时是否应该将整数数值归一化到特定的范围(对于类型
gl.FLOAT和gl.HALF_FLOAT,此参数无效)。正交化,true,false, [1, 2] => [1/根号5, 2/根号5] - stride:顶点之间的偏移量,每个点的信息所占的BYTES
- offset:顶点属性数组中第一部分的字节偏移量,每个点的信息,从第几个BYTES开始数
在上述的代码中,是将a_position这个顶点的位置属性告诉了webgl,进行了形状的绘制。绘制效果如下所示:
在上述的案例中,顶点坐标只包含了位置信息,其实每个店都可以包含它的颜色信息,这里我们更改下顶点坐标:
let vertices = [
// x y r g b
-0.5, 0.0, 1.0, 0.0, 0.0, // 第一个点
0.5, 0.0, 0.0, 1.0, 0.0, // 第二个点
0.0, 0.8, 0.0, 0.0, 1.0, // 第三个点
]
vertices = new Float32Array(vertices)
第一个点坐标(-0.5,0,0),颜色为红色;第二个点坐标(0.5,0.0),颜色为绿色;第三个点坐标(0.0,0.8),颜色为蓝色。 要获取到顶点信息中的字节偏移量
let FSIZE = vertices.BYTES_PER_ELEMENT
则原来用来解析顶点数据的函数应改为:
gl.vertexAttribPointer(
a_position,
2,
gl.FLOAT,
false,
5 * FSIZE, //区别在这里
0
)
如若要增加颜色的赋值,需要增加如下的解析。
let a_color = gl.getAttribLocation(gl.program, 'a_color')
gl.vertexAttribPointer(
a_color,
3,
gl.FLOAT,
false,
5 * FSIZE,
2 * FSIZE
)
gl.enableVertexAttribArray(a_color)
最后,看下完整的js代码和效果
import initShaders from '../initShaders.js'
let canvas = document.getElementById('webgl')
let gl = canvas.getContext('webgl')
// vertex shader
let vertexShader = `
attribute vec2 a_position;
attribute vec3 a_color;
varying vec3 v_color;
void main() {
v_color = a_color;
gl_Position = vec4(a_position, 0.0, 1.0);
}
`
// fragment shader
let fragmentShader = `
precision mediump float;
varying vec3 v_color;
void main() {
gl_FragColor = vec4(v_color, 1.0);
}
`
initShaders(gl,vertexShader,fragmentShader)
// 清空画布
gl.clearColor(0.0,0.0,0.0,1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
let vertices = [
// x y r g b
-0.5,0.0,1.0,0.0,0.0, // 第一个点
0.5,0.0,0.0,1.0,0.0, // 第二个点
0.0,0.8,0.0,0.0,1.0, // 第三个点
]
vertices = new Float32Array(vertices)
let FSIZE = vertices.BYTES_PER_ELEMENT
let buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER,buffer)
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW)
let a_position = gl.getAttribLocation(gl.program,'a_position')
let a_color = gl.getAttribLocation(gl.program,'a_color')
gl.vertexAttribPointer(
a_position, //location: vertex Shader里面attribute变量的location
2, //size: attribute变量的长度(vec2)
gl.FLOAT, //type: buffer里面数据的类型
false, //normalized: 正交化,true,false, [1, 2] => [1/根号5, 2/根号5]
5 * FSIZE, //stride:每个点的信息所占的BYTES
0 //offset: 每个点的信息,从第几个BYTES开始数
)
gl.vertexAttribPointer(
a_color,
3,
gl.FLOAT,
false,
5 * FSIZE,
2 * FSIZE
)
gl.enableVertexAttribArray(a_position)
gl.enableVertexAttribArray(a_color)
gl.drawArrays(gl.TRIANGLES,0,3)
3.用三角形绘制“圆”
在开头写到所有的多边形都可以看成是n个三角形组成,圆也是。这里我们造出一圈点,点的颜色随机生成,然后组成圆形。
// 随机生成 n 个点
let n = 20
let R = 0.8 // 圆的半径
let vertices = []
for (let i = 0; i < n; i++) {
let deg = 2 * Math.PI / n * i
let x = Math.cos(deg) * R
let y = Math.sin(deg) * R
let r = (Math.random() - 0.5) * 2 + 0.8
let g = (Math.random() - 0.5) * 2
let b = (Math.random() - 0.5) * 2
vertices.push(x, y, r, g, b)
}
vertices = new Float32Array(vertices)
Math.random()生成0到1之间的数字。这里,n越大,越接近圆。
在开头gl.drawArrays(mode, first, count);里面提到的mode,我们这里一一试下,看效果都是啥样的,直观看。
- gl.drawArrays(gl.POINTS,0,n)
- gl.drawArrays(gl.LINES, 0, n)
- gl.drawArrays(gl.LINE_STRIP, 0, n)
- gl.drawArrays(gl.LINE_LOOP, 0, n)
- gl.drawArrays(gl.TRIANGLES, 0, n)
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, n)
- gl.drawArrays(gl.TRIANGLE_FAN,0,n)
参考代码来自 webgl-tutorial-github