很遗憾,没有办法上传画布/图像的一部分。
WebGL 所基于的 OpenGL ES 2.0 没有提供这样做的方法。 OpenGL ES 3.0 确实提供了一种将较小的源矩形上传到纹理或纹理的一部分的方法,因此也许下一个版本的 WebGL 将提供该功能。
现在您可以有一个单独的画布来帮助上传。首先调整画布的大小以匹配您要上传的部分
canvasForCopying.width = widthToCopy;
canvasForCopying.height= heightToCopy;
然后将要复制的画布部分复制到画布上进行复制
canvasForCopying2DContext.drawImage(
srcCanvas, srcX, srcY, widthToCopy, heightToCopy,
0, 0, widthToCopy, heightToCopy);
然后使用它上传到你想要的纹理。
gl.texSubImage2D(gl.TEXTURE_2D, 0, destX, destY, gl.RGBA, gl.UNSIGNED_BYTE,
canvasForCopying);
getImageData 可能会更慢,因为浏览器必须调用readPixels 来获取图像数据,这会导致图形管道停止。对于drawImage,浏览器不必这样做。
至于为什么texImage2D 有时会比texSubImage2D 快,这取决于驱动程序/GPU,但显然有时texImage2D 可以使用DMA 实现,而texSubImage2D 不能。 texImage2D 也可以通过制作新纹理并懒惰地丢弃旧纹理来流水线化,而 texSubImage2D 不能。
我个人不会担心,但如果您想检查,请使用texImage2D 和texSubImage2D 上传数以万计的纹理(不要只计时一个,因为图形是流水线的)。如果您的纹理很大并且您要更新的部分小于纹理的 25%,您可能会发现 texSubImage2D 更快。至少这是我上次检查时发现的。大多数当前的驱动程序至少进行了优化,如果您调用 texSubImage2D 并且碰巧要替换整个内容,它们将在内部调用 texImage2D 代码。
更新
你可以做几件事
-
对于图像,您可以使用fetch 和ImageBitmap 将图像的一部分加载到ImageBitamp 中,然后您可以将其上传。获取图像一部分的示例
在下面的示例中,我们调用 fetch,然后获取 Blob 并使用该 blob 生成仅包含图像一部分的 ImageBitmap。结果可以传递给texImage2D,但为了简洁起见,示例仅在二维画布中使用它。
fetch('https://i.imgur.com/TSiyiJv.jpg', {mode: 'cors'})
.then((response) => {
if (!response.ok) {
throw response;
}
return response.blob();
})
.then((blob) => {
const x = 451;
const y = 453;
const width = 147;
const height = 156;
return createImageBitmap(blob, x, y, width, height);
}).then((bitmap) => {
useit(bitmap);
}).catch(function(e) {
console.error(e);
});
// -- just to show we got a portion of the image
function useit(bitmap) {
const ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
ctx.drawImage(bitmap, 0, 0);
}
-
在 WebGL2 中有 gl.pixelStorei 设置
UNPACK_ROW_LENGTH // 源的一行有多少像素
UNPACK_SKIP_ROWS // 从源头开始跳过多少行
UNPACK_SKIP_PIXELS // 从源左侧跳过多少像素
所以使用这 3 个设置,您可以告诉 webgl2 源更宽,但您想要的部分更小。您将较小的宽度传递给texImage2D,上面的 3 个设置有助于告诉 WebGL 如何退出较小的部分以及从哪里开始。