【WebGL之巅】19-颜色与纹理-使用多幅纹理

By yesmore on 2021-07-30
阅读时间 7 分钟
文章共 1.5k
阅读量

对应《WebGL编程指南》代码:21-MultiTexture

要点:纹理映射、多个纹理单元使用

知识点

一、区别单个纹理单元

1、片元着色器能够访问两个纹理

2、最终的片元颜色由两个纹理上的纹素颜色共同决定

3、initTextures(gl, n)函数创建了两个纹理对象

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
function initTextures(gl, n) {
var texture0 = gl.createTexture(); //创建纹理对象
var texture1 = gl.createTexture();

//获取u_Sampler的存储位置
var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');
var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');

var image0 = new Image();//创建一个image对象
var image1 = new Image();

//注册图像加载时间的响应函数
image0.onload = function () {
loadTexture(gl, n, texture0, u_Sampler0, image0, 0);
};
image1.onload = function () {
loadTexture(gl, n, texture1, u_Sampler1, image1, 1);
};

//浏览器开始加载图像
image0.src = '../resources/sky.jpg';
image1.src = '../resources/circle.gif';

return true;
}

二、片元着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//片元着色器程序
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform sampler2D u_Sampler0;\n' +
'uniform sampler2D u_Sampler1;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n'+
' vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n'+
' vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n'+
' gl_FragColor = color0 * color1;\n'+
'}\n';

1、定义两个uniform变量

2、在void main()函数中,从两个纹理中取出纹素颜色,分别存储到color0和color1中

3、使用两个纹素计算最终的片元颜色(gl_FragColor

​ 这里使用的是颜色矢量的乘法——两个矢量中对应的分量相乘作为新矢量的分量。

1
2
3
vec4 color0   r1   g1    b1    a1
vec4 color1 r2 g2 b2 a2
X r1*r2 g1*g2 b1*b2 a1*a2

​ 因为矩形顶点在两幅纹理图像上的纹理坐标是完全相同的,所以initVertexBuffers(gl)并没有改变。

三、关于loadTexture()函数

​ 由于程序是异步进行的,我们无法预测哪一幅纹理图像先被加载完成。只有当两幅纹理图像都完成加载时,程序才会开始绘图,为此,程序定义了以下两个变量:

1
2
//标记纹理单元是否已经就绪
var g_texUnit0 = false, g_texUnit1 = false;

​ 当任意一幅纹理加载完成时,就触发onload事件并调用响应函数loadTexture()。该函数首先根据纹理单元编号0或1来将g_texUnit0g_texUnit1赋值为true(0先加载完就赋值g_texUnit0为true;1同理)。

1
2
3
4
5
6
7
8
9
10
if(texUnit == 0)
{
gl.activeTexture(gl.TEXTURE0);
g_texUnit0 = true;
}
else
{
gl.activeTexture(gl.TEXTURE1);
g_texUnit1 = true;
}

​ 接着,纹理单元编号texUnit被赋给了uniform变量。

1
gl.uniform1i(u_Sampler, texUnit);

实例

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MultiTexture</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use the browser supporting "canvas".
</canvas>

<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script src="MultiTexture.js"></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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//MultiTexture.js
//顶点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec2 a_TexCoord;\n' +
'varying vec2 v_TexCoord;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' v_TexCoord = a_TexCoord;\n' +
'}\n';

//片元着色器程序
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform sampler2D u_Sampler0;\n' +
'uniform sampler2D u_Sampler1;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n'+
' vec4 color0 = texture2D(u_Sampler0, v_TexCoord);\n'+
' vec4 color1 = texture2D(u_Sampler1, v_TexCoord);\n'+
' gl_FragColor = color0 * color1;\n'+
'}\n';

function main() {
var canvas = document.getElementById("webgl");
if (!canvas) {
console.log("Failed to retrieve the <canvas> element");
return;
}

var gl = getWebGLContext(canvas);
if (!gl) {
console.log("Failed to get the rendering context for WebGL");
return;
}

if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("Failed to initialize shaders.");
return;
}

//设置顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}

// Specify the color for clearing <canvas>
gl.clearColor(0.0, 0.0, 0.0, 1.0);

// Set texture
if (!initTextures(gl, n)) {
console.log('Failed to intialize the texture.');
return;
}
}

function initVertexBuffers(gl){
var verticesTexCoords = new Float32Array(
[
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0,
]
);
var n=4;//顶点数目

//创建缓冲区对象
var vertexTexCoordBuffer = gl.createBuffer();

//将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);

//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
//将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE*4, 0);
//连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);

//将纹理坐标分配给a_TexCoord并开启它
var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');

gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE*4, FSIZE*2);
gl.enableVertexAttribArray(a_TexCoord);

return n;
}

function initTextures(gl, n) {
var texture0 = gl.createTexture(); //创建纹理对象
var texture1 = gl.createTexture();

//获取u_Sampler的存储位置
var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');
var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');

var image0 = new Image();//创建一个image对象
var image1 = new Image();

//注册图像加载时间的响应函数;注意最后一个参数是纹理单元编号
image0.onload = function () {
loadTexture(gl, n, texture0, u_Sampler0, image0, 0);
};
image1.onload = function () {
loadTexture(gl, n, texture1, u_Sampler1, image1, 1);
};

//浏览器开始加载图像
image0.src = '../resources/sky.jpg';
image1.src = '../resources/circle.gif';

return true;
}

//标记纹理单元是否已经就绪
var g_texUnit0 = false, g_texUnit1 = false;

function loadTexture(gl, n, texture, u_Sampler, image, texUnit){
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);//对纹理图像进行y轴反转

if(texUnit == 0)
{
gl.activeTexture(gl.TEXTURE0);
g_texUnit0 = true;
}
else
{
gl.activeTexture(gl.TEXTURE1);
g_texUnit1 = true;
}

//向target绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);

//配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
//配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);

//将0号纹理传递给着色器
gl.uniform1i(u_Sampler, texUnit);

gl.clear(gl.COLOR_BUFFER_BIT);

if(g_texUnit0 && g_texUnit1)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);//绘制矩形
}

效果

1


Tips: Please indicate the source and original author when reprinting or quoting this article.