首页 文章

如何在WebGL中使用texture2D渲染图像来保持宽高比?

提问于
浏览
0

我有一个宽度*高度= 1442 * 1303的图像,我能够通过webgl的texture2D读取它们并成功渲染到画布 .

在客户端,我有一个arraybuffer,可以获取size = width * height * 4的图像数据 .

因此,当我的画布宽度和高度为window.innerWidth * 0.90和window.innerHeight * 0.90时,如何保持图像的纵横比 .

另外,我必须通过WEBGL 2dTexture使用arraybuffer直接渲染,所以,我不能使用任何2d canvs API,如drawImage . 请提出建议 .

1 回答

  • 1

    这个问题确实有一百万个答案 .

    首先是图像的大小,然后是您决定绘制它的大小,以及画布的大小,然后是画布显示的大小 . 你使用的顶点的位置也可以是任何东西 .

    See this article on WebGL指出WebGL使用剪辑空间坐标(-1到1)和this article points out that the size a canvas is displayed is separate from its resolution .

    假设您想要尽可能大地绘制图像并使其适合画布 .

    首先让我们查看画布显示的大小

    var canvasDisplayWidth  = gl.canvas.clientWidth;
    var canvasDisplayHeight = gl.canvas.clientHeight;
    

    让我们假设我们想要尽可能大地绘制图像,因此首先尝试将宽度拟合到画布上

    var imageDisplayWidth  = canvasDisplayWidth;
    var imageDisplayHeight = img.height * imageDisplayWidth / img.width;
    

    现在让我们检查它是否合适?如果不是让我们使用高度

    if (imageDrawHeight > canvasDisplayHeight) { 
      imageDisplayHeight = canvasDisplayHeight;
      imageDisplayWidth  = img.width * imageDisplayHeight / img.height;
    }
    

    现在我们需要将 imageDisplayWidthimageDisplayHeight 转换为画布中像素的大小 . 注意:如果画布的显示大小与其分辨率相同,则可以跳过此步骤,因为显示大小和绘图大小将相同 .

    // make our image take into account the pixel aspect
    var canvasPixelsAcrossPerDisplayPixel = gl.canvas.width  / canvasDisplayWidth;
    var canvasPixelsDownPerDisplayPixel   = gl.canvas.height / canvasDisplayHeight;
    
    var imageDrawWidth  = imageDisplayWidth  * canvasPixelsAcrossPerDisplayPixel;
    var imageDrawHeight = imageDisplayHeight * canvasPixelsDownPerDisplayPixel;
    

    现在我们需要将其转换为剪辑空间

    var clipWidth  = imageDrawWidth  / canvas.width;
    var clipHeight = imageDrawHeight / canvas.height;
    

    现在,给定单位四元组,我们可以缩放它以适应该大小 .

    var m = m4.identity();
    
    // convert our square unit quad match the size we want
    m4.scale(m, [clipWidth, clipHeight, 1], m);
    
    // move our unit square from 0,0 (the center) to the bottom, top corner
    m4.translate(m, [-1, 1, 0], m);
    
    // scale our unit sqaure to cover the clip space
    m4.scale(m, [2, -2, 1], m);
    

    现在可以使用该矩阵和我们的单位四边形绘制

    var m4 = twgl.m4;
    var gl = twgl.getWebGLContext(document.getElementById("c"));
    var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
    
    var arrays = {
      position: {
        numComponents: 2,
        data: [
          0, 0,
          1, 0,
          0, 1,
          0, 1,
          1, 0,
          1, 1,
        ],
      },
    };
    var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
    
    // Lets make a texture using a 2d canvas
    // There's a circle in the middle. If our
    // code is correct it will be a circle when
    // drawn (not an oval or ellipse)
    var ctx = document.createElement("canvas").getContext("2d");
    ctx.canvas.width = 100;
    ctx.canvas.height = 75;
    ctx.fillStyle = "red";
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.fillStyle = "blue";
    ctx.fillRect(10, 10, ctx.canvas.width - 20, ctx.canvas.height - 20);
    ctx.strokeStyle = "yellow";
    ctx.lineWidth = 20;
    ctx.beginPath();
    ctx.arc(
      ctx.canvas.width / 2, ctx.canvas.height / 2,
      Math.min(ctx.canvas.width, ctx.canvas.height) / 2 - 20,
      0, Math.PI * 2, false);
    ctx.stroke();
    
    var img = ctx.canvas;
          
    var tex = twgl.createTexture(gl, {
      src: img,
    });
    
    var canvasDisplayWidth  = gl.canvas.clientWidth;
    var canvasDisplayHeight = gl.canvas.clientHeight;
    
    // Let's assume we want to draw the image as large as possible so
    // first try fitting the width to the canvas
    
    var imageDisplayWidth  = canvasDisplayWidth;
    var imageDisplayHeight = img.height * imageDisplayWidth / img.width;
    
    // Now let's check if it fit? If not let's use the height
    if (imageDisplayHeight > canvasDisplayHeight) { 
      imageDisplayHeight  = canvasDisplayHeight;
      imageDisplayWidth   = img.width * imageDisplayHeight / img.height;
    }
          
    // Now we need to convert `imageDisplayWidth` and `imageDisplayHeight` to the size of pixels
    // in the canvas. Note: If the canvas is being displayed the same size
    // as the its resolution you can skip this step
    
    var canvasPixelsAcrossPerDisplayPixel = gl.canvas.width  / canvasDisplayWidth;
    var canvasPixelsDownPerDisplayPixel   = gl.canvas.height / canvasDisplayHeight;
    
    var imageDrawWidth  = imageDisplayWidth  * canvasPixelsAcrossPerDisplayPixel;
    var imageDrawHeight = imageDisplayHeight * canvasPixelsDownPerDisplayPixel;     
    
    // Now we need to convert that to clip space
    
    var clipWidth  = imageDrawWidth  / gl.canvas.width;
    var clipHeight = imageDrawHeight / gl.canvas.height;
    
    // Now, given a unit quad we can just scale it to fit that size.
    var m = m4.identity();
    
    // convert our square unit quad to something to match the image's aspect
    m4.scale(m, [clipWidth, clipHeight, 1], m);
    
    // move our unit square from 0,0 (the center) to the bottom, left corner
    m4.translate(m, [-1, 1, 0], m);
    
    // scale our unit square to cover the clip space
    m4.scale(m, [2, -2, 1], m); 
    
    var uniforms = {
      texture: tex,
      matrix: m,
    };
    
    gl.useProgram(programInfo.program);
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, uniforms);
    twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
    
    <script id="vs" type="notjs">
    attribute vec4 position; 
    
    uniform mat4 matrix; 
    varying vec2 v_texcoord; 
    
    void main() {
      gl_Position = matrix * position; 
      
      // using position since we know it's a unit quad 
      v_texcoord = position.xy;
    }
    </script>
    <script id="fs" type="notjs">
    precision mediump float; 
    
    uniform sampler2D texture; 
    
    varying vec2 v_texcoord; 
    
    void main() { 
      gl_FragColor = texture2D(texture, v_texcoord); 
    }
    </script>
    <script src="https://twgljs.org/dist/twgl-full.min.js"></script>
    <canvas id="c" width="50" height="100" style="width: 300px; height: 150px; border: 1px solid black;"></canvas>
    

相关问题