最终的实现效果如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>test</title>
</head>
<body>
<canvas id="canvas" width="768" height="768"></canvas>
</body>
<script>
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
// var mat4=mat4.create();
var textureLoc = 0;
var attribOutUV = 0;
var uniformTexture0 = 0;
var uniformTexture1 = 0;
var texture0 = null;
var texture1 = null;
var count = 0;
var vsString = `
attribute vec4 a_position;
attribute vec2 outUV;
varying vec2 inUV;
void main(void){
gl_Position = a_position;
inUV = outUV;
}
`;
var fsString = `
precision mediump float;
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform float anim;
varying vec2 inUV;
void main(void){
vec4 color0 =texture2D(texture0,inUV);
vec4 color1 =texture2D(texture1, vec2(inUV.x + anim, inUV.y));
gl_FragColor = color0 + color1 ;
}
`;
// 入口函数
async function main() {
initWebgl();
initShader();
await initBuffer();
// 此处的绘制必须异步的,等待纹理加载完毕后,才进行绘制。否则会出现部分纹理不显示的情况
draw();
window.requestAnimationFrame(animation);
}
main();
// webgl初始化函数
function initWebgl() {
// 确定可视域范围
gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);
}
// shader初始化函数
function initShader() {
const vsShader = gl.createShader(gl.VERTEX_SHADER);
const fsShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vsShader, vsString);
gl.shaderSource(fsShader, fsString);
gl.compileShader(vsShader);
if (!gl.getShaderParameter(vsShader, gl.COMPILE_STATUS)) {
throw Error("顶点着色器错误:" + gl.getShaderInfoLog(vsShader));
}
gl.compileShader(fsShader);
if (!gl.getShaderParameter(fsShader, gl.COMPILE_STATUS)) {
throw Error("片元着色器错误:" + gl.getShaderInfoLog(fsShader));
}
const program = gl.createProgram();
gl.attachShader(program, vsShader);
gl.attachShader(program, fsShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw Error("program连接错误:" + gl.getShaderInfoLog(vsShader));
}
gl.useProgram(program);
gl.program = program;
}
// 数据缓存区的初始化函数
async function initBuffer() {
const pointPosition = new Float32Array([
-0.5, 0.5, 0, 1, 0, 1,
-0.5, -0.5, 0, 1, 0, 0,
0.5, -0.5, 0, 1, 1, 0,
0.5, 0.5, 0, 1, 1, 1,
-0.5, 0.5, 0, 1, 0, 1,
0.5, -0.5, 0, 1, 1, 0,
]);
const aPosition = gl.getAttribLocation(gl.program, "a_position");
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
gl.enableVertexAttribArray(aPosition);
// gl.vertexAttribPointer(aPosition,4,gl.FLOAT,false,16,0)
gl.vertexAttribPointer(aPosition, 4, gl.FLOAT, false, 6 * 4, 0);
attribOutUV = gl.getAttribLocation(gl.program, "outUV");
gl.enableVertexAttribArray(attribOutUV);
gl.vertexAttribPointer(attribOutUV, 2, gl.FLOAT, false, 6 * 4, 4 * 4);
uniformTexture0 = gl.getUniformLocation(gl.program, "texture0");
uniformTexture1 = gl.getUniformLocation(gl.program, "texture1");
texture0 = await initTexture("http://localhost:3000/test2.jpg");//底图
texture1 = await initTexture("http://localhost:3000/fog.png");//雾气纹理
}
// webgl绘制函数
function draw() {
// 初始化画布
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
const uniformAnim = gl.getUniformLocation(gl.program, "anim");
//更新雾气纹理偏移量
count = count + 0.01;
gl.uniform1f(uniformAnim, count);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture0);
gl.uniform1i(uniformTexture0, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.uniform1i(uniformTexture1, 1);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
async function initTexture(src) {
return new Promise((reslove, reject) => {
const image = new Image();
//解决读取远程图片链接的跨域问题
image.crossOrigin = "Anonymous";
image.src = src;
image.onload = function () {
reslove(handleLoadTexture(image));
};
image.onerror = reject;
});
}
function handleLoadTexture(img) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
//将图片坐标系转化为纹理坐标系。图标坐标系与纹理坐标系x轴方向相同,y轴相反。这个方法就是把y坐标取负值
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,666);
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);
return texture;
}
function animation() {
draw();
window.requestAnimationFrame(animation);
}
</script>
</html>