Как нарисовать 2D изображение с помощью TWGL (библиотека помощников WebGL)

Есть десятки примеров того, как рисовать трехмерные объекты с помощью TWGL https://github.com/greggman/twgl.js/tree/master/examples. Но как я могу нарисовать 2D-изображение с ним? Особенно меня интересует, как это сделать без использования шейдеров?

1 ответ

Решение

Единственная цель TWGL - сделать WebGL менее многословным, но WebGL, в основном, делает только одну вещь - рисовать вещи с помощью предоставляемых вами шейдеров. Таким образом, вы действительно ничего не можете сделать в TWGL без шейдеров так же, как вы ничего не можете сделать в WebGL без шейдеров.

Если вы не хотите писать какие-либо шейдеры, вам нужно использовать трехмерную библиотеку более высокого уровня, такую ​​как three.js или pixi.js или p5.js или даже что-то вроде Unity3D, поскольку они поставляют шейдеры для вас.

В противном случае вот код, который в основном реализует Canvas 2D drawImage функция в WebGL с использованием TWGL

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, gl.TRIANGLES, bufferInfo);
  
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/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>

Другие вопросы по тегам