代码之家  ›  专栏  ›  技术社区  ›  S.C.

如何在WebGL中缩放纹理?

  •  0
  • S.C.  · 技术社区  · 7 年前

    我的质地是800x600。如何在WebGL上缩放它 <canvas> 在另一个尺寸和保持原来的纵横比?假设绘图缓冲区和画布具有相同的尺寸。

    1 回复  |  直到 6 年前
        1
  •  3
  •   gman    7 年前

    考虑到WebGL只关心clipsapce坐标,您可以只绘制一个2单元的四元体(-1到+1),并根据画布的方面与图像的方面对其进行缩放。

    换言之

      const canvasAspect = canvas.clientWidth / canvas.clientHeight;
      const imageAspect = image.width / image.height;
    
      let scaleY = 1;
      let scaleX = imageAspect / canvasAspect;
    

    请注意,您需要决定如何调整图像。 scaleY= 1 意味着图像总是垂直和水平的,不管它是什么。

    如果希望它水平放置,则需要使scalex=1

      let scaleX = 1;
      let scaleY = canvasAspect / imageAspect;
    

    如果你想的话 contain 然后

      let scaleY = 1;
      let scaleX = imageAspect / canvasAspect;
      if (scaleX > 1) {
        scaleY = 1 / scaleX;
        scaleX = 1;
      }
    

    如果你想的话 cover 然后

      let scaleY = 1;
      let scaleX = imageAspect / canvasAspect;
      if (scaleX < 1) {
        scaleY = 1 / scaleX;
        scaleX = 1;
      }
    

    let scaleMode = 'fitV';
    
    const gl = document.querySelector("canvas").getContext('webgl');
    const vs = `
    attribute vec4 position;
    uniform mat4 u_matrix;
    varying vec2 v_texcoord;
    void main() {
      gl_Position = u_matrix * position;
      v_texcoord = position.xy * .5 + .5;  // because we know we're using a -1 + 1 quad
    }
    `;
    const fs = `
    precision mediump float;
    varying vec2 v_texcoord;
    uniform sampler2D u_tex;
    void main() {
      gl_FragColor = texture2D(u_tex, v_texcoord);
    }
    `;
    
    let image = { width: 1, height: 1 }; // dummy until loaded
    const tex = twgl.createTexture(gl, {
      src: 'https://i.imgur.com/TSiyiJv.jpg',
      crossOrigin: 'anonymous',
    }, (err, tex, img) => {
      // called after image as loaded
      image = img;
      render();
    });
    
    const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
      position: {
        numComponents: 2,
        data: [
          -1, -1,  // tri 1
           1, -1,
          -1,  1,      
          -1,  1,  // tri 2
           1, -1,
           1,  1,
        ],
      }
    });
    
    
    function render() {
      // this line is not needed if you don't
      // care that the canvas drawing buffer size
      // matches the canvas display size
      twgl.resizeCanvasToDisplaySize(gl.canvas);
    
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      gl.useProgram(programInfo.program);
      twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    
      const canvasAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
      const imageAspect = image.width / image.height;
      let scaleX;
      let scaleY;
    
      switch (scaleMode) {
        case 'fitV':
          scaleY = 1;
          scaleX = imageAspect / canvasAspect;
          break;
        case 'fitH':
          scaleX = 1;
          scaleY = canvasAspect / imageAspect;
          break;
        case 'contain':
          scaleY = 1;
          scaleX = imageAspect / canvasAspect;
          if (scaleX > 1) {
            scaleY = 1 / scaleX;
            scaleX = 1;
          }
          break;
        case 'cover':
          scaleY = 1;
          scaleX = imageAspect / canvasAspect;
          if (scaleX < 1) {
            scaleY = 1 / scaleX;
            scaleX = 1;
          }
          break;
      }
      
      twgl.setUniforms(programInfo, {
        u_matrix: [
          scaleX, 0, 0, 0,
          0, -scaleY, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ],
      });
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    }
    
    render();
    window.addEventListener('resize', render);
    document.querySelectorAll('button').forEach((elem) => {
      elem.addEventListener('click', setScaleMode);
    });
    
    function setScaleMode(e) {
     scaleMode = e.target.id;
     render();
    }
    html, body { 
      margin: 0;
      height: 100%;
    }
    canvas {
      width: 100%;
      height: 100%;
      display: block;
    }
    .ui {
      position: absolute;
      left: 0;
      top: 0;
    }
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    <canvas></canvas>
    <div class="ui">
      <button id="fitV">fit vertical</button>
      <button id="fitH">fit horizontal</button>
      <button id="contain">contain</button>
      <button id="cover">cover</button>
    </div>

    上面的代码使用4x4矩阵应用比例

      gl_Position = u_matrix * position;
    

    它可以很容易地直接通过天平。

      uniform vec2 scale;
      ...
      gl_Position = vec4(scale * position.xy, 0, 1);