绘制矩形
矩形的绘制 = 绘制两个(或多个)三角形
基本三角形构建 #
一个矩形由两个共线的三角形组成,即 V0, V1, V2, V3,其中 V0 -> V1 -> V2 代表三角形A,V0 -> V2 -> V3代表三角形B。
组成三角形的顶点要按照一定的顺序绘制。默认情况下,WebGL 会认为顶点顺序为逆时针时代表正面,反之则是背面,区分正面、背面的目的在于,如果开启了背面剔除功能的话,背面是不会被绘制的。
每个三角形由三个顶点组成,两个矩形共需要六个顶点:
let positions = [
30, 30, 255, 0, 0, 1, //V0
30, 300, 255, 0, 0, 1, //V1
300, 300, 255, 0, 0, 1, //V2
30, 30, 0, 255, 0, 1, //V0
300, 300, 0, 255, 0, 1, //V2
300, 30, 0, 255, 0, 1 //V3
]
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制矩形</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="shader-source" id="vertexShader">
precision mediump float;
attribute vec2 a_Position;
attribute vec2 a_Screen_Size;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
vec2 position = (a_Position / a_Screen_Size) * 2.0 - 1.0;
position = position * vec2(1.0, -1.0);
gl_Position = vec4(position, 0.0, 1.0);
gl_PointSize = 10.0;
v_Color = a_Color;
}
</script>
<script type="shader-source" id="fragmentShader">
precision mediump float;
varying vec4 v_Color;
void main() {
vec4 color = v_Color / vec4(255, 255, 255, 1);
gl_FragColor = color;
}
</script>
<script type="module">
import { getCanvas, resizeCanvas, getWebGLContext, createShaderFromScript, createProgram } from '../utils/webgl-helper.js'
const canvas = getCanvas('#canvas')
resizeCanvas(canvas)
const gl = getWebGLContext(canvas)
const vertexShader = createShaderFromScript(gl, gl.VERTEX_SHADER, 'vertexShader')
const fragmentShader = createShaderFromScript(gl, gl.FRAGMENT_SHADER, 'fragmentShader')
const program = createProgram(gl, vertexShader, fragmentShader)
gl.useProgram(program)
let a_Position = gl.getAttribLocation(program, 'a_Position')
let a_Screen_Size = gl.getAttribLocation(program, 'a_Screen_Size')
let a_Color = gl.getAttribLocation(program, 'a_Color')
gl.vertexAttrib2f(a_Screen_Size, canvas.width, canvas.height)
gl.enableVertexAttribArray(a_Position)
gl.enableVertexAttribArray(a_Color)
const buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 24, 0)
gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, 24, 8)
// 静态绘制两个三角形,需要六个节点
let positions = [
30, 30, 255, 0, 0, 1, //V0
30, 300, 255, 0, 0, 1, //V1
300, 300, 255, 0, 0, 1, //V2
30, 30, 0, 255, 0, 1, //V0
300, 300, 0, 255, 0, 1, //V2
300, 30, 0, 255, 0, 1 //V3
]
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, positions.length / 6)
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
索引方式绘制 #
在绘制一个矩形的时候,实际上只需要 V0, V1, V2, V3 四个顶点,而基于基于基本三角形构建的方式我们却存储了六个顶点,对内存造成了浪费。
WebGL 除了提供 gl.drawArrays
按顶点绘制的方式以外,还提供了一种按照顶点索引进行绘制的方法:gl.drawElements
,可以避免重复定义顶点:
void gl.drawElements(mode, count, type, offset)
- mode: 指定绘制图元的类型:点、线、三角形;
- count: 指定绘制图形的顶点个数;
- type:指定索引缓冲区中值的类型,常用的两个值:
gl.UNSIGNED_BYTE
(无符号8位整数) 和gl.UNSIGNED_SHORT
(无符号16位整数); - offset:指定索引数组中开始绘制的位置,以字节为单位;
核心代码:
// 顶点数组
let positions = [
30, 30, 255, 0, 0, 1, //V0
30, 300, 255, 0, 0, 1, //V1
300, 300, 255, 0, 0, 1, //V2
300, 30, 0, 255, 0, 1 //V3]
]
// 索引数组
let indices = [
0, 1, 2,
0, 2, 3
]
// 索引 buffer
const indicesBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW)
// drawElements
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
基于三角带绘制矩形 #
// 三角带绘制矩形,注意顶点位置
let positions = [
30, 300, 255, 0, 0, 1, //V1
300, 300, 255, 0, 0, 1, //V2
30, 30, 255, 0, 0, 1, //V0
300, 30, 0, 255, 0, 1 //V3
]
// 绘制:gl.TRIANGLE_STRIP
gl.drawArrays(gl.TRIANGLE_STRIP, 0, positions.length / 6)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
基于三角扇绘制 #
// 三角扇绘制矩形,注意顶点位置
let positions = [
30, 30, 255, 0, 0, 1, //V0
30, 300, 255, 0, 0, 1, //V1
300, 300, 255, 0, 0, 1, //V2
300, 30, 0, 255, 0, 1 //V3
]
// 绘制:gl.TRIANGLE_FAN
gl.drawArrays(gl.TRIANGLE_FAN, 0, positions.length / 6)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11