【问题标题】:How can I upload a texture in WebGL2 using a Pixel Buffer Object?如何使用像素缓冲区对象在 WebGL2 中上传纹理?
【发布时间】:2017-09-17 17:03:46
【问题描述】:

目前,使用texImage2d 上传大型 4096x4096 纹理非常慢,在将纹理发送到 GPU 时锁定主线程并最终导致卡顿。

据我了解,WebGL2 能够使用 PBO(像素缓冲区对象)以更有效的方式在 GPU 上创建纹理。但是,我无法在网上找到任何有关如何执行此操作的示例。

我在OpenGL 中找到了有关如何实现此目的的很好描述,但我不确定如何继续使用 WebGL API。

我想使用CanvasImageBitmap 作为纹理数据的来源。

到目前为止,我正在通过将纹理绘制到画布上进行测试,然后使用canvas.toBlob() 后跟FileReaderreadAsArrayBuffer 将图像转换为arrayBuffer。然后,一旦我实际上有一个有效的缓冲区,我就会尝试创建 PBO 并上传它。

我的代码的相关部分如下所示:

  var buf = gl.createBuffer();
  var view = new Uint8Array(ArrayBuffer);

  gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, buf);
  gl.bufferData(gl.PIXEL_UNPACK_BUFFER, view, gl.STATIC_DRAW);

  gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.width, this.height, 0, this.format, this.type, 0);

但这会返回错误:

GL_INVALID_OPERATION : glTexImage2D: pixel unpack buffer is not large enough

我真的不知道我是否正确地接近它,所以任何帮助将不胜感激。

【问题讨论】:

    标签: webgl2


    【解决方案1】:

    我可能是错的,但如果 WebGL 中用于上传数据的 PBO 比 texImage2D 快,我会感到惊讶。 PBO 本身存在于另一个过程中。要将数据传输到该进程,需要使用 gl.bufferData 将数据从 JavaScript 进程复制到 GPU 进程。在幕后,这两种方法的复制是相同的。

    在本机 OpenGL ES 中它可以更快的原因是因为您可以调用 glMapBufferRange 将该 PBO 映射到您的进程的内存中,但是在浏览器中没有办法有效和安全地做到这一点,所以没有 gl.mapBufferRange 在WebGL2

    来自规范

    MapBufferRange,尤其是它的只读和只写模式, 无法安全地暴露给 JavaScript。 GetBufferSubData 替换它以从 GPU 取回数据。

    5.14 没有 MapBufferRange

    MapBufferRangeFlushMappedBufferRangeUnmapBuffer 入口点已从 WebGL 2.0 API 中删除。以下枚举值也被删除:BUFFER_ACCESS_FLAGSBUFFER_MAP_LENGTHBUFFER_MAP_OFFSETMAP_READ_BITMAP_WRITE_BITMAP_INVALIDATE_RANGE_BITMAP_INVALIDATE_BUFFER_BITMAP_FLUSH_EXPLICIT_BITMAP_UNSYNCHRONIZED_BIT

    可以使用getBufferSubData 入口点读取缓冲区数据,而不是使用MapBufferRange

    对于上传 4096x4096 纹理,可以考虑制作一个空纹理(将null 传递给texImage2D,然后使用texSubImage2d 每帧上传一部分纹理以避免卡顿?

    至于问题本身,通过PBO上传纹理数据是使用gl.bufferData将数据复制到PBO的问题。

    const vs = `#version 300 es
    void main() {
      gl_Position = vec4(0, 0, 0, 1);
      gl_PointSize = 128.0;
    }
    `;
    
    const fs = `#version 300 es
    precision mediump float;
    
    uniform sampler2D u_tex;
    
    out vec4 outColor;
    
    void main() {
      // twizzle colors to show we went through shader
      outColor = texture(u_tex, gl_PointCoord.xy).gbra;
    }
    `;
    
    const gl = document.querySelector("canvas").getContext("webgl2");
    
    // compiles shaders, links program, looks up locations
    const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    
    // make a 2d canvas
    const ctx = document.createElement("canvas").getContext("2d");
    ctx.arc(150, 75, 60, 0, Math.PI * 2);
    ctx.fillStyle = "red";
    ctx.fill();
    ctx.lineWidth = 20;
    ctx.strokeStyle = "yellow";
    ctx.stroke();
    ctx.fillStyle = "cyan";
    ctx.font = "bold 48px sans-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText("FFFF", 150, 75);
    
    const pbo = gl.createBuffer();
    const data = ctx.getImageData(0, 0, 300, 150).data; 
    gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
    gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW);
    // data is now in PBO
    
    const tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    // take data from PBO
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 300, 150, 0, 
                  gl.RGBA, gl.UNSIGNED_BYTE, 0);
    gl.generateMipmap(gl.TEXTURE_2D);
    
    // draw a single point, uniforms default to 0 so sampler
    // will use texture unit 0
    gl.useProgram(programInfo.program)
    gl.drawArrays(gl.POINTS, 0, 1);
    canvas { border: 1px solid black; }
    <script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
    <canvas></canvas>

    【讨论】:

      猜你喜欢
      • 2012-01-13
      • 2017-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-24
      • 1970-01-01
      • 2023-03-29
      相关资源
      最近更新 更多