上节我们知道 webgl 通过 texture unit 来支持多个纹理图片。这节我们用 2 张图片来合成一个叠加的效果。
// fragment shader
var FRAGMENT_SHADER_SOURCE =
'precision mediump float;\n' +
'varying vec2 v_TexCoord;\n' +
'uniform sampler2D u_Sampler0;\n' +
'uniform sampler2D u_Sampler1;\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';
合成后颜色是 color0 * color1, 这是 component-wise 的乘法。
假设 color0 = (r1, g1, b1, a1) , color1 = (r2, g2, b2, a2)
则 color0 * color1 = (r1 * r2, g1 * g2, b1 * b2, a1 * a2)
完整代码
// vertex shader
var VERTEX_SHADER_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';
// fragment shader
var FRAGMENT_SHADER_SOURCE =
'precision mediump float;\n' +
'varying vec2 v_TexCoord;\n' +
'uniform sampler2D u_Sampler0;\n' +
'uniform sampler2D u_Sampler1;\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';
var canvas = document.getElementById("canvas");
var gl = canvas.getContext('webgl');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
if (!initShaders(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) {
alert('Failed to init shaders');
}
var vertices = new Float32Array([
-0.5, 0.5, 0.0, 1.0, // 前 2 位是位置坐标, 后 2 位是纹理坐标
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0
]);
initVertexBuffers(gl, vertices);
var u_Sampler0 = gl.getUniformLocation(gl.program, 'u_Sampler0');
var u_Sampler1 = gl.getUniformLocation(gl.program, 'u_Sampler1');
var image0Loaded = false;
var image1Loaded = false;
var image0 = new Image();
image0.onload = function () {
image0Loaded = true;
loadTexture(image0, 0, u_Sampler0);
};
image0.src = 'images/sky.jpg';
var image1 = new Image();
image1.onload = function () {
image1Loaded = true;
loadTexture(image1, 1, u_Sampler1);
};
image1.src = 'images/circle.gif';
function initVertexBuffers(gl, vertices) {
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create buffer object');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var FSIZE = vertices.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 4 * FSIZE, 0);
gl.enableVertexAttribArray(a_Position);
var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 4 * FSIZE, 2 * FSIZE);
gl.enableVertexAttribArray(a_TexCoord);
}
function loadTexture(image, textureUnit, u_Sampler) {
var texture = gl.createTexture();
// 翻转 Y 轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
if (textureUnit == 0) {
gl.activeTexture(gl.TEXTURE0);
} else if (textureUnit == 1) {
gl.activeTexture(gl.TEXTURE1);
}
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
gl.uniform1i(u_Sampler, textureUnit);
if (image1Loaded && image1Loaded) {
// 两个图片都加载完毕后再绘制
draw();
}
}
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}