如何通过WebGL帧缓冲区异步拼接图像并保留已渲染内容?
WebGL异步拼接图像并保留历史渲染内容的问题
这是我此前问题《Draw textures to canvas async / in sequence deletes old textures》的后续,采用朋友推荐的不同方案。我刚接触WebGL,还请包涵。
我的目标
- 异步加载图像
- 将这些图像以并排“平铺”的方式渲染到单个WebGL画布上,每张图像包含指定其在画布中渲染位置的坐标
- 每次异步加载图像后,将整个画布视为单个纹理,通过shader对整体纹理进行图像处理
使用帧缓冲区(framebuffer)
我的理解是可以创建帧缓冲区,将纹理渲染到其中,再将帧缓冲区渲染到目标纹理,最后将目标纹理渲染到屏幕。
// 首先创建帧缓冲区,以及要渲染到的目标纹理,并将纹理附加到帧缓冲区 const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); const targetTexture = gl.createTexture(); gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0 );
我的思路是每次加载图像后,将其创建为纹理,启用顶点属性后调用drawArrays绘制到帧缓冲区,再解绑帧缓冲区调用drawArrays渲染到屏幕,但此处存在困惑:
// 假设我们现在有一个包含多个瓦片图片URL的数组: tiles.forEach((tile) => { const image = new Image(); image.onload = () => render(image, tile); image.src = tile.path; }); function render(tileImage: HTMLImageElement, tile: Tile) { // 获取顶点数据的位置 var positionLocation = gl.getAttribLocation(program, 'a_position'); var texcoordLocation = gl.getAttribLocation(program, 'a_texCoord'); // 创建缓冲区来存储二维裁剪空间的点 var positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // 设置与图像尺寸相同的矩形(细节见问题附录) setRectangle( gl, tile.position.x, tile.position.y, tileImage.width, tileImage.height ); // 为矩形提供纹理坐标 var texcoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, ]), gl.STATIC_DRAW ); // 创建纹理并绑定到WebGL上下文 const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // 设置参数以支持任意尺寸的图像渲染 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.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tileImage ); // 获取uniform变量的位置 var resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); var textureSizeLocation = gl.getUniformLocation(program, 'u_textureSize'); // 告知WebGL如何从裁剪空间转换为像素 gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // 使用我们的着色器程序 gl.useProgram(program); // 启用位置属性 gl.enableVertexAttribArray(positionLocation); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); // 启用纹理坐标属性 gl.enableVertexAttribArray(texcoordLocation); gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0); // 设置分辨率和图像尺寸 gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); gl.uniform2f(textureSizeLocation, 256, 256); // 绑定帧缓冲区并绘制——绘制到帧缓冲区? gl.bindFramebuffer(gl.FRAMEBUFFER, fb); gl.drawArrays(gl.TRIANGLES, 0, 6); // 解绑帧缓冲区并绘制——绘制到画布? gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.drawArrays(gl.TRIANGLES, 0, 6); }
实际运行时,每次加载新图像后,之前渲染的图像会消失。我查阅过相关讨论,但未找到针对简单2D图像异步渲染到WebGL画布的方案。此外,有人推荐先通过2D Canvas API将图像绘制到2D画布,再将其作为WebGL的单一纹理来源,但我不理解该方案的优势。
请问如何实现将这些图像异步拼接并渲染到单个WebGL画布中?
附录:
export function setRectangle( gl: WebGLRenderingContext, x: number, y: number, width: number, height: number ) { const x1 = x, x2 = x + width, y1 = y, y2 = y + height; gl.bufferData( gl.ARRAY_BUFFER, // prettier-ignore new Float32Array([ x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW ); }
内容的提问来源于stack exchange,提问作者Seth Lutske
相关产品推荐
相关产品推荐

