WebGL 绘制图像

WebGL Drawing an image

我是 WebGL 的新手,我之前在 Java 中使用过 OpenGL。我一直在尝试编写一个简单的函数,在特定位置以特定大小和旋转绘制图像。但是在网上搜索了一段时间后,我的代码还是不行。

目前,我已经成功地绘制了一幅图像,但该图像距离正确的位置、尺寸和旋转角度都不正确。我已经失去了对代码做什么和需要什么的概述,因为我使用了许多不同教程中的代码,因为我没有找到一个教程符合我的所有规范。

我知道图像加载部分肯定有效。我只需要帮助来制作

的功能

有人可以帮我吗?

你应该 read up on WebGL especially about matrices.

无论如何,这是来自 canvas 2d API 的“drawImage”,使用完整的变换堆栈在 WebGL 中重新编写。

换句话说,在 Canvas2D 中你可以这样做

ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.scale(w, h);
ctx.drawImage(img, x, y);
ctx.restore();

下面你可以这样做

save();
translate(x, y);
rotate(angle);
scale(w, h);
drawImage(targetWidth, targetHeight, tex, texWidth, texHeight, x, y);
restore();

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

// a unit quad
var arrays = {
  position: { 
    numComponents: 2, 
    data: [
      0, 0,  
      1, 0, 
      0, 1, 
      0, 1, 
      1, 0,  
      1, 1,
    ],
  },
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
  
// Let's use a 2d canvas for a texture just so we don't have to download anything
var ctx = document.createElement("canvas").getContext("2d");
var w = 128;
var h = 64;
ctx.canvas.width = w;
ctx.canvas.height = h;
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "green";
ctx.fillRect(w / 8, h / 8, w / 8 * 6, h / 8 * 6);
ctx.fillStyle = "red";
ctx.fillRect(w / 4, h / 4, w / 2, h / 2);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "20px sans-serif";
ctx.fillStyle = "yellow";
ctx.fillText("texture", w / 2, h / 2);
      
var tex = twgl.createTexture(gl, { src: ctx.canvas });

// Implement a matrix stack like Canvas2d
var matrixStack = [ m4.identity() ];      
      
function render(time) {      
  var t = time * 0.001;

  var texWidth     = w;
  var texHeight    = h;
  var targetWidth  = gl.canvas.width;
  var targetHeight = gl.canvas.height;
      
  save();
  translate(
    (Math.sin(t * 0.9) * 0.5 + 0.5) * targetWidth,
    (Math.sin(t * 0.8) * 0.5 + 0.5) * targetHeight);
  rotate(t * 0.7);
  scale(
    Math.sin(t * 0.7) * 0.5 + 1,
    Math.sin(t * 0.6) * 0.5 + 1);
  
  // scale and rotate from center of image
  translate(texWidth * -0.5, texHeight * -0.5);
    
  drawImage(
    targetWidth, targetHeight,
    tex, texWidth, texHeight,  
    0, 0);
    
  restore();
    
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

function getCurrentMatrix() {
  return matrixStack[matrixStack.length - 1];
}

function save() {
  matrixStack.push(m4.copy(getCurrentMatrix()));
}

function restore() {
  matrixStack.pop();
  if (!matrixStack.length) {
    matrixStack.push(m4.identity());
  }
}

function translate(x, y) {
  var m = getCurrentMatrix();
  m4.translate(m, [x, y, 0], m);
}

function scale(x, y) {
  var m = getCurrentMatrix();
  m4.scale(m, [x, y, 1], m);
}

function rotate(radians) {
  var m = getCurrentMatrix();
  m4.rotateZ(m, radians, m);
}
      

// 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
) {
  // handle case where only x, y are passed in
  // as in ctx.drawIimage(img, x, y);
  if (srcWidth === undefined) {
    srcWidth  = texWidth;
    srcHeight = texHeight;
  }
  
  // handle case where only x, y, width, height are passed in
  // as in ctx.drawIimage(img, x, y, width, height);
  if (dstX === undefined) {
    dstX = srcX;
    dstY = srcY;
    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);

  // Add in global matrix
  m4.multiply(mat, getCurrentMatrix(), 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);
  
}
html, body, canvas {
  margin: 0; width: 100%; height:100%; display: block;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.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() {
  gl_FragColor = texture2D(texture, texcoord);
}
</script>
<canvas id="c"></canvas>

here's an article describing how it works