webgl-多重纹理

83 阅读2分钟

最终的实现效果如下

1710061995047.gif

<!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>
​