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

时间:2016-02-23 11:31:00

标签: html5 image-processing canvas webgl

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

在客户端,我有一个数组缓冲区,可以获取大小=宽*高* 4的图像数据。

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

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

1 个答案:

答案 0 :(得分: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>