如何在2D画布和WebGL画布之间共享纹理?

6
我正在使用WebGL开发一个浏览器游戏,建议的方法是使用普通的HTML元素或在WebGL画布上方覆盖一个2D画布来实现UI / HUD。虽然这很好,但是如何处理纹理共享呢?例如,在游戏中有掉落在地面上的物品,因此它们存在于世界中,并且必须作为WebGL纹理存在。
但是,当您拾取它们时,它们的图标将用作HUD中位置位于屏幕底部的能力栏中。因此,您必须有一个Canvas2D纹理以绘制UI,并有一个WebGLTexture纹理以在WebGL画布上进行绘制。
目前,我通过两次加载纹理来解决这个问题,一次作为游戏中的WebGL纹理,一次作为画布纹理。但是我认为这种解决方案并不是非常可扩展,因为在最坏的情况下,我将不得不两次加载游戏中的每个纹理,这将使游戏的内存使用量翻倍。
以下是我加载纹理的两种方法供参考:
// Load canvas texture
static createCanvasFromImage(filename: string) {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d");

    let newImg = new Image();

    newImg.onload = function() {
      canvas.width = newImg.width;
      canvas.height = newImg.height;

      context.drawImage(newImg, 0, 0);

      resolve(canvas);
    }

    newImg.src = filename;
  });
}

.

// Load WebGL texture
static loadImageAndCreateTextureInfo(gl: WebGLRenderingContext, url: string) {
  var tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  // Fill the texture with a 1x1 blue pixel.
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  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);

  // Don't know size until it loads
  var textureInfo = { width: 1, height: 1, texture: tex };

  var img = new Image();
  img.addEventListener('load', function() {
    textureInfo.width = img.width;
    textureInfo.height = img.height;

    gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
  });
  img.src = url;

  return textureInfo;
}

这种情况通常如何处理?有没有办法分享这些纹理,以便每个纹理只加载一次并且可以在任何画布上使用?


WebGL纹理对象的数据存储在GPU内存中,并由图形驱动程序管理。 - Rabbid76
ImageBitmaps 可以被 Webgl 和 2D 上下文同时使用。 - Kaiido
你可以将图像绘制到第三个画布上,不向用户显示,并通过 ctx.drawImage(textureCanvas, 0, 0) 在你的 Canvas 2D 中使用它,在 WebGL 中使用 gl.texImage2D - Georgi B. Nikolov
1个回答

2

我不知道这是否有太大的帮助,但也许如果您有一个单独的函数,两个函数都调用它来创建图像,然后将其推送到资产数组中?

比如说,您可以像这样编写代码:

async newImage (url) {

  const img = new Image();

  img.src = url;

  // Wait for the image to load

  await new Promise(resolve => img.onload = () => resolve());

  // Once loaded, push to "assets" (Or whatever you'd like to call it) and return image

  assets.push(img);

  return img;
}

// WebGL Texture, for example

static async loadImageAndCreateTextureInfo (gl: WebGLRenderingContext, url: string) {

  var tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);

  // Fill the texture with a 1x1 blue pixel.

  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  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);

  // Don't know size until it loads

  var textureInfo = { width: 1, height: 1, texture: tex };

  // "await" makes the code wait for an action to be completed

  const img = await this.newImage(url);
  
  textureInfo.width = img.width;
  textureInfo.height = img.height;

  gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);

  return textureInfo;
}

我还没有测试过这段代码,所以它可能有效也可能无效,但你可以尝试一下 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接