WebGL绘制图像

时间:2016-05-13 20:37:55

标签: javascript canvas webgl

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

目前,我已成功绘制图像,但该图像并非接近于正确的位置,具有正确的大小和旋转。我已经失去了对代码的作用和需求的概述,因为我使用了许多不同教程的代码,因为我没有找到一个具有我所有规范的教程。

我知道图像加载部分可以正常工作。我只需要帮助制作一个

的功能
  • 设置顶点和片段着色器(用于绘制宽度纹理)
  • 翻译,调整大小并将其旋转到正确的位置,大小和旋转
  • 并绘制

有人可以帮我吗?

1 个答案:

答案 0 :(得分:0)

您应该read up on WebGL,特别是关于matrices

无论如何,这里" drawImage"来自使用完整转换堆栈在WebGL中重写的canvas 2d API。

换句话说,在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 = twgl.getWebGLContext(document.getElementById("c"));
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(getCurrentMatrix(), mat, 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);
  
}

canvas { border: 1px solid black; }

<script src="https://twgljs.org/dist/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" width="640" height="480"></canvas>
&#13;
&#13;
&#13;

here's an article describing how it works