2

二维画布提供了一个名为 draw image 的 api: context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height); 其中sx图像剪辑开始的位置。

http://www.w3schools.com/tags/canvas_drawimage.asp ,

我正在尝试使用 webgl 使用texImage2D渲染 2D 图像。我想检查是否有办法使用 webgl 实现剪辑

我正在使用以下教程使用 webgl 渲染 2d 图像。 http://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

原图:

原始图像

用 drawImage(2D) 剪辑:

在此处输入图像描述

使用 webgl 进行剪辑:

在此处输入图像描述

var gl,program,positionLocation,originalImageTexture,canvas;
var x = 10;
var y = 20;
function setupWebGL(){

	var canvas = document.getElementById("canvas");
	gl = canvas.getContext("webgl");
  	if (!gl) {
    	return;
  	}
  	// setup GLSL program
  	var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  	gl.useProgram(program);
  	// look up where the vertex data needs to go.
  	positionLocation = gl.getAttribLocation(program, "a_position");
 	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
  	var texture = gl.createTexture();
  	gl.bindTexture(gl.TEXTURE_2D, texture);
  
  	// lookup uniforms
  	var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
  	textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
  	colorLocation = gl.getUniformLocation(program, "u_color");
   	// set the resolution
  	gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
  
  	// Set the parameters so we can render any size image.
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);


  	gl.enable(gl.BLEND);
  	gl.blendEquation( gl.FUNC_ADD );
  	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  	gl.disable(gl.DEPTH_TEST);
}

    function draw() {
    // use canvas to simulate an image
  	var image = document.createElement("canvas");
    document.body.appendChild(image); // so we can see the source image
    image.width  = 200;
    image.height = 150;
    var ctx   = image.getContext("2d");
    ctx.fillRect(0, 0, image.width, image.height);
    for (var py = 0; py < image.height; py += 25) {
      for (var px = 0; px < image.width; px += 25) {
        ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                 (px / image.width  * 255 | 0) + "," + 
                                  255 + ")";
        ctx.beginPath();
        ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
        ctx.fill();
      }
    }
      
  	setupWebGL();
      
    var srcX = 12;
    var srcY = 35;
    var srcWidth = 75;
    var srcHeight = 50;
      
    var dstX = 100;
    var dstY = 110;
    var dstWidth  = srcWidth;
    var dstHeight = srcHeight;
  		
    var u0 = 50 / image.width;
    var v0 = 0 / image.height;
    var u1 = (50 + image.width) / image.width;
    var v1 = (0 + image.height) / image.height;
      
  	// provide texture coordinates for the rectangle.
  	var texCoordBuffer = gl.createBuffer();
  	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([u0, v0, u1,v0, u0,v1, u0,v1, u1,v0, u1,v1]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

  
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
  
    // set the size of the image
    gl.uniform2f(textureSizeLocation, image.width, image.height);
    // Create a buffer for the position of the rectangle corners.
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

    setRectangle( gl, x, y, image.width, image.height);
    
 
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  
}

    function setRectangle(gl, x, y, width, height) {
	var x1 = x;
	var x2 = x + width;
	var y1 = y;
	var y2 = y + height;
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2]), gl.STATIC_DRAW);
}

draw();
canvas { border: 1px solid black; }
<canvas width="400" height="300" id="canvas"></canvas>
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;

uniform vec2 u_resolution;

varying vec2 v_texCoord;

void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points.
   v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

4

1 回答 1

5

您需要调整纹理坐标以选择您感兴趣的纹理部分

纹理坐标从 0 到 1,所以如果你想从drawImage给定转换

drawImage(image, srcX, srcY, srcWidth, srcHeight, 
          dstX, dstY, dstWidth, dstHeight);

在 WebGL 中,您必须使用这些值调整 texcoordsrc以及使用这些值绘制位置的顶点坐标dst

u0 = srcX / image.width;
v0 = srcY / image.height;

u1 = (srcX + srcWidth) / image.width;
v1 = (srcY + srcHeight) / image.height;

现在更新你的 texcoords。在您链接到的示例中

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  u0,  v0,
  u1,  v0,
  u0,  v1,
  u0,  v1,
  u1,  v0,
  u1,  v1]), gl.STATIC_DRAW);

您还可以在着色器中添加一些数学。使用与此处开始的文章相同的技术,您可以使用数学来调整着色器中的纹理坐标,就像那些文章正在调整位置一样。

因此,例如,您可以让 UV​​ 坐标像原来一样从 0 变为 1,但更新着色器,这样您就可以传入偏移量和缩放比例,将它们相乘以获得所需的位置。或者,如果您继续阅读这些文章,您可以使用矩阵更灵活地操作它们。

同样对于目标大小,您可以在着色器中使用各种数学运算来移动一个简单的单位正方形,并将其缩放和大小调整到您想要的任何大小,而不是更新顶点。

至于您发布此行的代码

var u1 = (50 + image.width) / image.width;

基本上是说你想从右边缘 50 像素的纹理中读取像素。这就是你变黑的原因。

第二个问题是这条线

setRectangle( gl, x, y, image.width, image.height);

除非您打算拉伸图像,否则您要求将剪切的图像(例如image.width - 50)绘制为image.width像素,所以如果您的图像是 75 像素,您将获得 25 像素的源拉伸到 75 像素的目标

不确定这是否是您想要的。这是固定版本

请注意,我使用画布制作图像,因为 dataURL 使编辑非常痛苦。

var gl,program,positionLocation,originalImageTexture,canvas;
var x = 10;
var y = 20;
function setupWebGL(){

	var canvas = document.getElementById("canvas");
	gl = canvas.getContext("webgl");
  	if (!gl) {
    	return;
  	}
  	// setup GLSL program
  	var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  	gl.useProgram(program);
  	// look up where the vertex data needs to go.
  	positionLocation = gl.getAttribLocation(program, "a_position");
 	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
  	var texture = gl.createTexture();
  	gl.bindTexture(gl.TEXTURE_2D, texture);
  
  	// lookup uniforms
  	var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
  	textureSizeLocation = gl.getUniformLocation(program, "u_textureSize");
  	colorLocation = gl.getUniformLocation(program, "u_color");
   	// set the resolution
  	gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
  
  	// Set the parameters so we can render any size image.
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);


  	gl.enable(gl.BLEND);
  	gl.blendEquation( gl.FUNC_ADD );
  	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  	gl.disable(gl.DEPTH_TEST);
}

    function draw() {
    // use canvas to simulate an image
  	var image = document.createElement("canvas");
    document.body.appendChild(image); // so we can see the source image
    image.width  = 200;
    image.height = 150;
    var ctx   = image.getContext("2d");
    ctx.fillRect(0, 0, image.width, image.height);
    for (var py = 0; py < image.height; py += 25) {
      for (var px = 0; px < image.width; px += 25) {
        ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                 (px / image.width  * 255 | 0) + "," + 
                                  255 + ")";
        ctx.beginPath();
        ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
        ctx.fill();
      }
    }
      
  	setupWebGL();
      
    var srcX = 50;
    var srcY = 0;
    var srcWidth = image.width - 50;
    var srcHeight = image.height;
      
    var dstX = x;
    var dstY = y;
    var dstWidth  = srcWidth;
    var dstHeight = srcHeight;
  		
	var u0 = srcX / image.width;
 	var v0 = srcY / image.height;
	var u1 = (srcX + srcWidth)  / image.width;
 	var v1 = (srcY + srcHeight) / image.height;
	
  	// provide texture coordinates for the rectangle.
  	var texCoordBuffer = gl.createBuffer();
  	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([u0, v0, u1,v0, u0,v1, u0,v1, u1,v0, u1,v1]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

  
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
  
    // set the size of the image
    gl.uniform2f(textureSizeLocation, image.width, image.height);
    // Create a buffer for the position of the rectangle corners.
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

    setRectangle( gl, dstX, dstY, dstWidth, dstHeight);
   
 
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  
}

    function setRectangle(gl, x, y, width, height) {
	var x1 = x;
	var x2 = x + width;
	var y1 = y;
	var y2 = y + height;
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2]), gl.STATIC_DRAW);
}

draw();
canvas { border: 1px solid red; }
<canvas width="400" height="300" id="canvas"></canvas>
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;

uniform vec2 u_resolution;

varying vec2 v_texCoord;

void main() {
   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = a_position / u_resolution;

   // convert from 0->1 to 0->2
   vec2 zeroToTwo = zeroToOne * 2.0;

   // convert from 0->2 to -1->+1 (clipspace)
   vec2 clipSpace = zeroToTwo - 1.0;

   gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points.
   v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

我还想再次强调这不是我通常会这样做的方式。就像我之前在这个特殊情况下所说的那样,我会使用一个单位平方位置和一个单位平方作为 texcoords。然后我会使用矩阵来翻译和抵消两者。这将让我做 2D 画布 API 可以做的所有事情。缩放图像、剪切图像、旋转图像、翻转图像。它甚至可以让我做画布 API 不能做的事情,比如旋转矩形内的纹理。

var gl,program,positionLocation,originalImageTexture,canvas;
function setupWebGL(){

	var canvas = document.getElementById("canvas");
	gl = canvas.getContext("webgl");
  	if (!gl) {
    	return;
  	}
  	// setup GLSL program
  	var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  	gl.useProgram(program);
  	// look up where the vertex data needs to go.
  	positionLocation = gl.getAttribLocation(program, "a_position");
 	texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
  	var texture = gl.createTexture();
  	gl.bindTexture(gl.TEXTURE_2D, texture);
  
  	// lookup uniforms
  	matrixLocation = gl.getUniformLocation(program, "u_matrix");
  	texMatrixLocation = gl.getUniformLocation(program, "u_texMatrix");
  
  	// Set the parameters so we can render any size image.
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  	// provide texture coordinates for the rectangle.
  	var texCoordBuffer = gl.createBuffer();
  	gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,gl.RGBA, gl.UNSIGNED_BYTE, image);
  
    // Create a buffer for the position of the rectangle corners.
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0 , 1, 1]), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

  	gl.enable(gl.BLEND);
  	gl.blendEquation( gl.FUNC_ADD );
  	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  	gl.disable(gl.DEPTH_TEST);
}

var originX, originY, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, angleInRadians,texAngle;

function draw(time) {
    time *= 0.001; // make time in seconds
  
  
    switch (((time / 4) | 0) % 5) {
      case 0:
        originX = 0;
        originY = 0;
        srcX = 50;
        srcY = 0;
        srcWidth = image.width - 50;
        srcHeight = image.height;
      
        dstX = 10;
        dstY = 20;
        dstWidth  = srcWidth;
        dstHeight = srcHeight;
            
        angleInRadians = 0;
        texAngle = 0;
        break;
      case 1:
         // clip it more
         srcX = 50 + Math.sin(time      ) * 20;
         srcY = 50 + Math.sin(time * 1.3) * 20;
         srcWidth  = image.width  - (50 + Math.sin(time      ) * 20) - srcX;
         srcHeight = image.height - (50 + Math.sin(time * 1.3) * 20) - srcY;
         dstWidth  = srcWidth;
         dstHeight = srcHeight;
         break;
      case 2:
        // spin image around top left
        angleInRadians = time;
        break;
      case 3:
        // spin image around center
        angleInRadians = time;
        dstX = 100;
        dstY = 100;
        originX = -srcWidth / 2;
        originY = -srcHeight / 2;
        break;
      case 4:
        // spin texture around center
        texAngle = -time;
        break;
    }
  
      
    var x = dstX;
    var y = dstY;
    // We have a 1 unit square. If will scale by destWidth and destHeight we'll stretch
    // it to the size we want
    var xScale = dstWidth;
    var yScale = dstHeight;
    
    // We also have a 1 unit square for the texcoords. We can scale that to clip
    var texXOff   = srcX      / image.width;
    var texYOff   = srcY      / image.height;
    var texXScale = srcWidth  / image.width;
    var texYScale = srcHeight / image.height;

    // Compute the matrices
    var projectionMatrix = make2DProjection(gl.canvas.clientWidth, gl.canvas.clientHeight);
    var translationMatrix = makeTranslation(x, y);
    var rotationMatrix = makeRotation(angleInRadians);
    var originMatrix = makeTranslation(originX, originY);
    var scaleMatrix = makeScale(xScale, yScale);
  
    // Multiply the matrices.
    var matrix = matrixMultiply(scaleMatrix, originMatrix);
    matrix = matrixMultiply(matrix, rotationMatrix);
    matrix = matrixMultiply(matrix, translationMatrix);
    matrix = matrixMultiply(matrix, projectionMatrix);
      
    // compute matrixes for texture
    var texPreRot = makeTranslation(-0.5, -0.5);
    var texRot    = makeRotation(texAngle);
    var texPostRot= makeTranslation(0.5, 0.5);
    var texOffMat = makeTranslation(texXOff, texYOff);
    var texScaleMat = makeScale(texXScale, texYScale);
  
    var texMatrix = matrixMultiply(texScaleMat, texOffMat);
    texMatrix = matrixMultiply(texMatrix, texPreRot);
    texMatrix = matrixMultiply(texMatrix, texRot);
    texMatrix = matrixMultiply(texMatrix, texPostRot);
    

    // Set the matrices
    gl.uniformMatrix3fv(matrixLocation, false, matrix);      
    gl.uniformMatrix3fv(texMatrixLocation, false, texMatrix);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
 
    requestAnimationFrame(draw);
}

    // use canvas to simulate an image
  	image = document.createElement("canvas");
    document.body.appendChild(image); // so we can see the source image
    image.width  = 200;
    image.height = 150;
    var ctx   = image.getContext("2d");
    ctx.fillRect(0, 0, image.width, image.height);
    for (var py = 0; py < image.height; py += 25) {
      for (var px = 0; px < image.width; px += 25) {
        ctx.fillStyle = "rgb(" + (py / image.height * 255 | 0) + "," + 
                                 (px / image.width  * 255 | 0) + "," + 
                                  255 + ")";
        ctx.beginPath();
        ctx.arc(px + 12, py + 12, 10, 0, Math.PI * 2);
        ctx.fill();
      }
    }
      
setupWebGL();
requestAnimationFrame(draw);
canvas { border: 1px solid red; }
<canvas width="400" height="300" id="canvas"></canvas>
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="//webglfundamentals.org/webgl/resources/webgl-2d-math.js"></script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;

uniform mat3 u_matrix;    // manipulates position
uniform mat3 u_texMatrix; // manipulates texcoords

varying vec2 v_texCoord;

void main() {
     gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);

   // pass the texCoord to the fragment shader
   // The GPU will interpolate this value between points.
   v_texCoord = (u_texMatrix * vec3(a_texCoord, 1)).xy;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

// our texture
uniform sampler2D u_image;

// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

于 2015-04-28T15:40:00.447 回答