Вам необходимо настроить координаты текстуры, чтобы выбрать интересующую вас часть текстуры. ва>.
Координаты текстуры идут от 0 до 1, поэтому, если вы хотите преобразовать из drawImage
, тогда дайте
drawImage(image, srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight);
В WebGL вам придется настроить texcoord, используя значения src
, и координаты вершины, где рисовать, используя значения 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. Затем я бы использовал матрицы для перевода и смещения обоих. Это позволило бы мне делать все, что может делать API 2D-холста. Масштабируйте изображение, обрезайте изображение, поворачивайте изображение, переворачивайте изображение. Это даже позволило бы мне делать то, что 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>
person
gman
schedule
28.04.2015