在 Webgl 中裁剪图像
Clipping images in Webgl
2-D canvas 提供了一个 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>
You need to adjust the texture coordinates to select the part of the texture you're interested in.
纹理坐标从 0 到 1,所以如果你想从 drawImage
转换,那么给定
drawImage(image, srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight);
在 WebGL 中,您必须使用 src
值调整纹理坐标,并使用 dst
值调整绘制位置的顶点坐标。
u0 = srcX / image.width;
v0 = srcY / image.height;
u1 = (srcX + srcWidth) / image.width;
v1 = (srcY + srcHeight) / image.height;
现在更新你的纹理坐标。在您链接到的示例中
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
u0, v0,
u1, v0,
u0, v1,
u0, v1,
u1, v0,
u1, v1]), gl.STATIC_DRAW);
您还可以向着色器添加一些数学运算。使用 the same techniques as the articles starting here 您可以使用数学来调整着色器中的纹理坐标,就像那些文章正在调整位置一样。
因此,例如,您可以让 UV 坐标像它们最初那样从 0 变为 1,但更新着色器,以便您可以传入一个偏移量和一个缩放比例,将它们相乘以获得您想要的位置。或者,如果您继续阅读这些文章,您可以使用矩阵更灵活地操作它们。
与目标大小类似,您可以在着色器中使用各种数学运算来移动一个简单的单位正方形,并将其缩放和大小调整为您想要的任何大小,而不是更新顶点。
至于你这一行贴的代码
var u1 = (50 + image.width) / image.width;
基本上是说您想从右边缘后 50 像素处的纹理读取像素。这就是你变黑的原因。
第二个问题是这一行
setRectangle( gl, x, y, image.width, image.height);
除非您打算拉伸图像,否则您会要求绘制要绘制 image.width
像素的裁剪图像(比如 image.width - 50
),因此如果您的图像是 75 像素,您将获得 25 像素的源拉伸到目标的 75 像素
不确定这是否是您想要的。这是固定版本
请注意,我使用 canvas 制作图像,因为 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>
我还想再次强调,我通常不会这样做。就像我之前针对这个特殊情况所说的那样,我将使用一个单位正方形位置和一个单位正方形来表示纹理坐标。然后我会使用矩阵来翻译和抵消两者。这将使我可以做 2D canvas API 可以做的所有事情。缩放图像、裁剪图像、旋转图像、翻转图像。它甚至可以让我做 canvas 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>
2-D canvas 提供了一个 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>
You need to adjust the texture coordinates to select the part of the texture you're interested in.
纹理坐标从 0 到 1,所以如果你想从 drawImage
转换,那么给定
drawImage(image, srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight);
在 WebGL 中,您必须使用 src
值调整纹理坐标,并使用 dst
值调整绘制位置的顶点坐标。
u0 = srcX / image.width;
v0 = srcY / image.height;
u1 = (srcX + srcWidth) / image.width;
v1 = (srcY + srcHeight) / image.height;
现在更新你的纹理坐标。在您链接到的示例中
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
u0, v0,
u1, v0,
u0, v1,
u0, v1,
u1, v0,
u1, v1]), gl.STATIC_DRAW);
您还可以向着色器添加一些数学运算。使用 the same techniques as the articles starting here 您可以使用数学来调整着色器中的纹理坐标,就像那些文章正在调整位置一样。
因此,例如,您可以让 UV 坐标像它们最初那样从 0 变为 1,但更新着色器,以便您可以传入一个偏移量和一个缩放比例,将它们相乘以获得您想要的位置。或者,如果您继续阅读这些文章,您可以使用矩阵更灵活地操作它们。
与目标大小类似,您可以在着色器中使用各种数学运算来移动一个简单的单位正方形,并将其缩放和大小调整为您想要的任何大小,而不是更新顶点。
至于你这一行贴的代码
var u1 = (50 + image.width) / image.width;
基本上是说您想从右边缘后 50 像素处的纹理读取像素。这就是你变黑的原因。
第二个问题是这一行
setRectangle( gl, x, y, image.width, image.height);
除非您打算拉伸图像,否则您会要求绘制要绘制 image.width
像素的裁剪图像(比如 image.width - 50
),因此如果您的图像是 75 像素,您将获得 25 像素的源拉伸到目标的 75 像素
不确定这是否是您想要的。这是固定版本
请注意,我使用 canvas 制作图像,因为 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>
我还想再次强调,我通常不会这样做。就像我之前针对这个特殊情况所说的那样,我将使用一个单位正方形位置和一个单位正方形来表示纹理坐标。然后我会使用矩阵来翻译和抵消两者。这将使我可以做 2D canvas API 可以做的所有事情。缩放图像、裁剪图像、旋转图像、翻转图像。它甚至可以让我做 canvas 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>