如何使用 TWGL (WebGL helper Library) 绘制二维图像

How to draw 2D image with TWGL (WebGL helper Library)

有许多关于如何使用 TWGL 绘制 3d 内容的示例 https://github.com/greggman/twgl.js/tree/master/examples。但是我怎样才能用它绘制二维图像呢?特别是我对如何在不使用着色器的情况下做到这一点很感兴趣?

TWGL 的唯一目的是让 WebGL 不那么冗长,但 WebGL 基本上只做一件事,那就是 draw stuff with shaders you provide。所以你真的不能在没有着色器的情况下在 TWGL 中做任何事情,就像你在没有着色器的情况下不能在 WebGL 中做任何事情一样。

如果您不想编写任何着色器,那么您需要使用更高级别的 3D 库,例如 three.js or pixi.js or p5.js or even something like Unity3D,因为它们会为您提供着色器。

否则这里有一些代码基本上使用 TWGL

在 WebGL 中实现 canvas 2d 的 drawImage 函数

var m4 = twgl.m4;
var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

// a unit quad
var bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
      
// we're only using 1 texture so just make and bind it now
var tex = twgl.createTexture(gl, {
  src: 'https://farm6.staticflickr.com/5695/21506311038_9557089086_m_d.jpg',
  crossOrigin: '', // not needed if image on same origin
}, function(err, tex, img) {
  // wait for the image to load because we need to know it's size
  startRendering(img);
});

function startRendering(img) {

  requestAnimationFrame(render);
  
  function render(time) {
    time *= 0.001;
    
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 
    
    drawImage(
      gl.canvas.width, gl.canvas.height,
      tex, img.width, img.height,
      Math.sin(time) * 50, Math.sin(time * 1.1) * 50);
    requestAnimationFrame(render);
  }
}
                             
                             
// we pass in texWidth and texHeight because unlike images
// we can't look up the width and height of a texture

// we pass in targetWidth and targetHeight to tell it
// the size of the thing we're drawing too. We could look 
// up the size of the canvas with gl.canvas.width and
// gl.canvas.height but maybe we want to draw to a framebuffer
// etc.. so might as well pass those in.

// srcX, srcY, srcWidth, srcHeight are in pixels 
// computed from texWidth and texHeight

// dstX, dstY, dstWidth, dstHeight are in pixels
// computed from targetWidth and targetHeight
function drawImage(
    targetWidth, targetHeight,
    tex, texWidth, texHeight,
    srcX, srcY, srcWidth, srcHeight,
    dstX, dstY, dstWidth, dstHeight) {
  if (srcWidth === undefined) {
    srcWidth = texWidth;
    srcHeight = texHeight;
  }
  if (dstX === undefined) {
    dstX = srcX;
    dstY = srcY;
    srcX = 0;
    srcY = 0;
  }
  if (dstWidth === undefined) {
    dstWidth = srcWidth;
    dstHeight = srcHeight;
  }      
      
  var mat  = m4.identity();
  var tmat = m4.identity();
  
  var uniforms = {
    matrix: mat,
    textureMatrix: tmat,
    texture: tex,
  };

  // these adjust the unit quad to generate texture coordinates
  // to select part of the src texture

  // NOTE: no check is done that srcX + srcWidth go outside of the
  // texture or are in range in any way. Same for srcY + srcHeight

  m4.translate(tmat, [srcX / texWidth, srcY / texHeight, 0], tmat);
  m4.scale(tmat, [srcWidth / texWidth, srcHeight / texHeight, 1], tmat);

  // these convert from pixels to clip space
  m4.ortho(0, targetWidth, targetHeight, 0, -1, 1, mat)

  // these move and scale the unit quad into the size we want
  // in the target as pixels
  m4.translate(mat, [dstX, dstY, 0], mat);
  m4.scale(mat, [dstWidth, dstHeight, 1], mat);

  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.setUniforms(programInfo, uniforms);
  twgl.drawBufferInfo(gl, bufferInfo);
  
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="not-js">
// we will always pass a 0 to 1 unit quad
// and then use matrices to manipulate it
attribute vec4 position;   

uniform mat4 matrix;
uniform mat4 textureMatrix;

varying vec2 texcoord;

void main () {
  gl_Position = matrix * position;
  
  texcoord = (textureMatrix * position).xy;
}
</script>
<script id="fs" type="not-js">
precision mediump float;

varying vec2 texcoord;
uniform sampler2D texture;

void main() {
  if (texcoord.x < 0.0 || texcoord.x > 1.0 ||
      texcoord.y < 0.0 || texcoord.y > 1.0) {
    discard;
  }
  gl_FragColor = texture2D(texture, texcoord);
}
</script>
<canvas id="c"></canvas>

有关更详细的说明,请参阅 this article on making a WebGL clone of drawImage from 2D canvas and this article on implementing a matrix stack like 2D canvas