第一个webgl程序

1

### 创建 WebGL上下文


1
2
3
4
5
6
7
8
9
<canvas id='glcanvas' width='600' height='600'></canvas>

<script>
var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');


</script>

创建顶点着色器和片段着色器

所有的 WebGL 程序都必须有一个顶点着色器和一个片段着色器。

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
<script id="vertexShader" type="x-shader/x-vertex">

attribute vec3 a_position;

void main(){
gl_Position = vec4(a_position,1.0);
gl_PointSize = 10.0;
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>


<script>

//顶点着色器源码
var vertexShaderSource = document.getElementById('vertexShader').innerText;

//片元着色器源码
var fragShaderSource = document.getElementById('fragmentShader').innerText;

</script>

### 编译着色器


1
2
3
4
gl.shaderSource(vShader, vertexShaderSource); //设置着色器的源码
gl.shaderSource(fShader, fragShaderSource);
gl.compileShader(vShader); //编译着色器
gl.compileShader(fShader);

### 创建程序对象和链接着色器


1
2
3
4
5
var shaderProgram = gl.createProgram(); //创建着色器程序
gl.attachShader(shaderProgram, vShader); //把着色器信息附加到着色器程序
gl.attachShader(shaderProgram, fShader);
gl.linkProgram(shaderProgram); //连接着色器程序
gl.useProgram(shaderProgram); //

建立缓冲

1
2
3
4
5
6
7
8
9
10
11
var vertices = [
0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0
];


var vertex_buffer = gl.createBuffer(); // 创建一个空的缓冲区对象来存储顶点缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // 绑定缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 将顶点数据传递到缓冲区

调用 gl.bufferData() 方法,把顶点数据写入当前绑定的 WebGLBuffer 对象中。这个调用告诉 WebGL,哪些数据保存在用 gl.createBuffer()创建的缓冲对象中。这样子会导致丢失上下文。

清空面板

1
2
3
//清空画板
gl.clearColor(0.0, 0.0, 0.0, 1.0); //设置清空颜色缓冲时的颜色
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.clear() 方法通过参数 gl.COLOR_BUFFER_BIT 指示 WebGL 把颜色缓冲清除为事先用gl.clearColoro() 函数定义的颜色。本例用 gl.clearColor()把黑色定义为清除颜色。因此,当调用 gl.clear()方法时,颜色缓冲清除为黑色。

clearColor

1
void gl.clearColor(red, green, blue, alpha);

参数

  • red 一个 GLclampf 类型的值,指定清除缓冲时的红色值。默认值:0。
  • green 一个 GLclampf 类型的值,指定清除缓冲时的绿色值。默认值:0。
  • blue 一个 GLclampf 类型的值,指定清除缓冲时的蓝色值。默认值:0。
  • alpha 一个 GLclampf 类型的值,指定清除缓冲时的不透明度。默认值:0

绘制场景

1
2
3
4
5
6
7
8
9
10
11
var a_position = gl.getAttribLocation(shaderProgram, "a_position");

//这里是设置获取数据的规则 第二个参数3 表示每个点定点有3个数据(对应vec3)
// 顶点着色器 会执行3次
gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_position); //激活属性。不激活的属性是不会被使用的


//开始画图
//3 表示要画 3 个点
gl.drawArrays(gl.TRIANGLES, 0, 3)

vertexAttribPointer

1
void gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
  • index 指定要修改的顶点属性的索引 , 可以通过 gl.getAttribLocation 获取

  • size 表示每个属性的大小或分量数。在本例中,每个属性的分量是3(因为每个顶点位置用x、y、z坐标表示)。

  • type 指定数组中每个元素的数据类型。gl.FLOAT 表示要把顶点缓冲对象中的值当作浮点数。如果我们传入的数据不是浮点数,则在顶点着色器中使用它之前,必须将其转换为浮点数

    • gl.BYTE: 有符号的8位整数,范围[-128, 127]
    • gl.SHORT: 有符号的16位整数,范围[-32768, 32767]
    • gl.UNSIGNED_BYTE: 无符号的8位整数,范围[0, 255]
    • gl.UNSIGNED_SHORT: 无符号的16位整数,范围[0, 65535]
    • gl.FLOAT: 32位IEEE标准的浮点数normalized
  • normalized 当转换为浮点数时是否应该将非浮点数统一转化成特定范围的浮点数。

    • 对于类型gl.BYTEgl.SHORT,如果是true则将值归一化为[-1, 1]
    • 对于类型gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT,如果是true则将值归一化为[0, 1]
    • 对于类型gl.FLOATgl.HALF_FLOAT,此参数无效
  • stride 当这个参数取值0时,就相当于 $ size * sizeof(type) $ 表示数据在内存中顺
序存放

  • offset 表示缓冲中的偏移值。如果数据从缓冲的开始位置存放,那么这个参数也设置为 0。

image-20240322202147785

完整代码

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<html>
<head>
<meta charset="utf-8">
<title></title>


<script id="vertexShader" type="x-shader/x-vertex">

attribute vec3 a_position;

void main(){
gl_Position = vec4(a_position,1.0);
gl_PointSize = 10.0;
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>

</head>
<body>

<canvas id='glcanvas' width='600' height='600'></canvas>


<script>
var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');


//顶点着色器源码
var vertexShaderSource = document.getElementById('vertexShader').innerText;

//片元着色器源码
var fragShaderSource = document.getElementById('fragmentShader').innerText;

//创建顶点着色器对象
var vShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vShader, vertexShaderSource); //引入顶点着色器源代码
gl.compileShader(vShader); ////编译顶点着色器

//创建片元着色器对象
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fShader, fragShaderSource); //引入片元着色器源代码
gl.compileShader(fShader); //编译片元着色器


var shaderProgram = gl.createProgram(); //创建程序对象program

//附着顶点着色器和片元着色器到program
gl.attachShader(shaderProgram, vShader);
gl.attachShader(shaderProgram, fShader);

gl.linkProgram(shaderProgram); // 链接program
gl.useProgram(shaderProgram); // 使用program


var vertices = [
0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0
];


var vertex_buffer = gl.createBuffer(); // 创建一个空的缓冲区对象来存储顶点缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // 绑定缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // 将顶点数据传递到缓冲区


//清空画板
gl.clearColor(0.0, 0.0, 0.0, 1.0); //设置清空颜色缓冲时的颜色
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);



var a_position = gl.getAttribLocation(shaderProgram, "a_position");

//这里是设置获取数据的规则 第二个参数3 表示每个点定点有3个数据(对应vec3)
// 顶点着色器 会执行3次
gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_position); //激活属性。不激活的属性是不会被使用的


//开始画图
//3 表示要画 3 个点
gl.drawArrays(gl.TRIANGLES, 0, 3)
</script>

</body>
</html>

类型化数组

上面代码中 Float32Array 到底是什么,以及为什么要用这个类型?

C和C++等程序设计语言是可以直接操作二进制数据。但是 JavaScript 语言并没有内置二进制数据的处理功能。js开发人员如果需要处理二进制数据,则要把数据转化成字符串,然后用JavaScript的charCodeAt()方法和位操作运算符(&、、<<和>>)进行处理。

下面这个示例说明如何获得用字符串表示的数据中某个位置的特定字节∶


1
var oneByte = str.charCodeAt(index) & 0xFF;

这个过程的开销太大,不是一个切实可行的解决方法。因此,引入了 JavaScript类型化数组类型,它是一个更为有效的二进制处理方法。


ArrayBuffer

new ArrayBuffer(length)

  • length 表示要创建的 ArrayBuffer 的大小,单位为字节。

类型化数组是建立在ArrayBuffer对象的基础上的。它的作用是,分配一段可以存放数据的连续内存区域。

1
2
// 生成了一段32字节的内存区域
var buf = new ArrayBuffer(32);

视图

ArrayBuffer作为内存区域,是不带类型的。需要为创建的ArrayBuffer指定一个类型,才能对其进行读写操作 ,这就叫做“视图”。

JavaScript 提供类型视图如下:

TYPE Description Value Range Size in bytes
Int8Array 8位有符号整数 -128 to 127 1字节
Uint8Array 8位无符号整数 0 to 255 1字节
Uint8ClampedArray 8位浮点数 0 to 255 1字节
Int16Array 16位有符号整数 -32768 to 32767 2字节
Uint16Array 16位无符号整数 0 to 65535 2字节
Int32Array 32位有符号整数 -231 to 231-1 4字节
Uint32Array 32位无符号整数 0 to 232 4字节
Float32Array 32位浮点数 “Isn’t this fun?” 4字节
Float64Array 64位浮点数 – is en-dash, — is em-dash 8字节

如果创建了一个 ArrayBuffer,并且以它为基础又创建了一个 Uint8Array、一个Uint16Array、一个 Uint32Array、一个 Float64Array 视图,他们之间的对应关系如下:


arraybuffer

数组缓冲区中的值的类型ArrayBuffer 的 对应关系

ArrayBuffer 数组缓冲区类型
Uint8Array gl.UNSIGNED_BYTE
Uint16Array gl.UNSIGNED_SHORT_5_6_5
gl.UNSIGNED_SHORT_4_4_4_4
gl.UNSIGNED_SHORT_5_5_5_1
gl.UNSIGNED_SHORT
ext.HALF_FLOAT_OES
Uint32Array gl.UNSIGNED_INT
ext.UNSIGNED_INT_24_8_WEBGL
Float32Array gl.FLOAT

第一个webgl程序
http://example.com/2024/05/23/webgl/第一个webgl程序/
Author
John Doe
Posted on
May 23, 2024
Licensed under