立方体有六个面,六个面都需要添加纹理。同时为了显示立方体的三维效果,我们还需要旋转一下立方体。代码如下所示:
# 1、顶点着色器代码
<script id="vertexShader" type="x-shader/x-vertex">
//浮点数设置为中等精度
precision mediump float;
//从js中传递过来的顶点坐标数据
attribute vec3 apos;
//沿x轴旋转的4维矩阵
uniform mat4 mx;
//沿y轴旋转的4维矩阵
uniform mat4 my;
//js中传递过来的纹理坐标数据
attribute vec2 inUV;
//插值处理后传递到片元着色器的纹理坐标
varying vec2 outUV;
void main() {
//两个旋转矩阵、顶点齐次坐标连乘
gl_Position = mx*my*vec4(apos, 1);
//插值处理
outUV=inUV;
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2、片元着色器代码
在片元着色器中接收来自顶点着色器中的纹理坐标数据,以及js
代码中传递过来的纹理数据,最终生成立方体的片元颜色。
<script id="fragmentShader" type="x-shader/x-fragment">
// 所有float类型数据的精度是lowp
precision mediump float;
varying vec2 outUV;
uniform sampler2D texture;
void main() {
gl_FragColor=texture2D(texture,outUV);
}
</script>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 3、javascript代码
- 获取
WebGL
上下文 在该函数中主要实现了如下功能:- 获取
WebGL
上下文 - 初始化着色器
- 顶点数据的构建及以着色器的交互
- 获取
function init() {
//通过getElementById()方法获取canvas画布
const canvas = document.getElementById('webgl');
//通过方法getContext()获取WebGL上下文
const gl = canvas.getContext('webgl');
//顶点着色器源码
const vertexShaderSource = document.getElementById('vertexShader').innerText;
//片元着色器源码
const fragShaderSource = document.getElementById('fragmentShader').innerText;
//初始化着色器
const program = initShader(gl, vertexShaderSource, fragShaderSource);
initBuffer(gl, program);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 立方体顶点数据的构建及与着色器的交互
该函数的主要功能可以分为以下几点
- 获取着色器中的变量地址
- 构建顶点数据和纹理坐标数据
- 构建顶点索引数据
- 创建顶点缓冲区
- 创建索引缓冲区
- 给着色器中的变量赋值
- 开启深度测试
- 加载纹理图片
WebGL
渲染
function initBuffer(gl, program) {
//获取顶点着色器的位置变量apos
const aposLocation = gl.getAttribLocation(program, 'apos');
const inUV = gl.getAttribLocation(program, 'inUV');
const texture = gl.getUniformLocation(program, 'texture');
//允许数据传递
gl.enableVertexAttribArray(aposLocation);
//允许数据传递
gl.enableVertexAttribArray(inUV);
//创建立方体的顶点坐标数据及纹理坐标数据
const data = new Float32Array([
-0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
-0.5, 0.5, 0.5, 1.0, 1.0,
-0.5, 0.5, -0.5, 0.0, 1.0,
-0.5, -0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, 0.5, 1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0,
0.5, 0.5, -0.5, 1.0, 1.0,
0.5, 0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, -0.5, 1.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 1.0,
0.5, 0.5, 0.5, 1.0, 1.0,
0.5, 0.5, -0.5, 1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 0.0,
-0.5, -0.5, 0.5, 0.0, 0.0,
-0.5, -0.5, -0.5, 0.0, 1.0,
0.5, -0.5, -0.5, 1.0, 1.0,
0.5, -0.5, 0.5, 1.0, 0.0
]);
//构建顶点索引数据
const indexdata = new Uint16Array([
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
])
//创建缓冲区对象
const buffer = gl.createBuffer();
//绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
//顶点数组data数据传入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
//创建索引缓冲区对象
const indexBuffer = gl.createBuffer();
//绑定索引缓冲区对象
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
// //顶点数组data数据传入索引缓冲区
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexdata, gl.STATIC_DRAW);
///缓冲区中的数据按照一定的规律传递给位置变量apos
gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 4 * 5, 0);
gl.vertexAttribPointer(inUV, 2, gl.FLOAT, false, 4 * 5, 4 * 3);
// gl.vertexAttribPointer(acolor, 4, gl.FLOAT, false, 4 * 9, 4*3);
gl.enable(gl.DEPTH_TEST);
// gl.enable(gl.CULL_FACE);
// render(gl, program, indexdata.length)
initTexture(gl, '../images/webgl/1.jpg', texture, function () {
render(gl, program, indexdata.length)
})
}
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
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
- 加载纹理图片
在该函数中主要是创建一个图片示例,最后将图片数据传递给
GPU
。- 创建纹理图像缓冲区
- 设置纹理图片上下反转
- 激活
0
号纹理单元 - 绑定纹理缓冲区
- 设置纹素格式,
jpg
格式对应gl.RGB
,将图片数据传递给GPU
。 - 设置纹理贴图填充方式
- 全局变量赋值,纹理缓冲区单元
TEXTURE0
中的颜色数据传入片元着色器
function initTexture(gl, src, attribute, callback) {
let img = new Image();
img.onload = function () {
let texture = gl.createTexture();//创建纹理图像缓冲区
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); //纹理图片上下反转
gl.activeTexture(gl.TEXTURE0);//激活0号纹理单元TEXTURE0
gl.bindTexture(gl.TEXTURE_2D, texture);//绑定纹理缓冲区
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.uniform1i(attribute, 0);
callback && callback();
};
img.src = src;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WebGL
渲染
function render(gl, program, count = 36) {
//沿x,y轴旋转
tranlate(gl, program);
//设置清屏颜色为黑色。
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// gl.drawArrays(gl.TRIANGLES, 0, count);
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, 0);
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
上面的js
代码只例举了部分,其他的函数代码实现,在前面的例子中都有列举。
最终立方体的纹理贴图效果如下所示:
参考
WebGL零基础入门教程(郭隆邦) (opens new window)
WebGL 入门与实践 (opens new window)
WebGL官方文档 (opens new window)