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

Rya*_*hel 6 html javascript textures canvas webgl

我正在使用 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;
  });
}
Run Code Online (Sandbox Code Playgroud)

// 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;
}
Run Code Online (Sandbox Code Playgroud)

通常如何处理这种情况?有什么方法可以共享这些纹理,以便每个纹理仅在内存中加载一次,并且可以在任一画布上使用?

小智 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;
}
Run Code Online (Sandbox Code Playgroud)

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