【问题标题】:How to share textures between a 2d Canvas and a WebGL canvas?如何在 2d 画布和 WebGL 画布之间共享纹理?
【发布时间】:2021-07-24 19:05:29
【问题描述】:

我正在使用 WebGL 开发浏览器游戏,the recommended advice 似乎是使用直接的 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 内存中,由图形驱动程序管理。
  • ImageBitmaps 可以被 webgl 和 2d 上下文使用。
  • 您可以将图像绘制到您不向用户显示的第三个画布上,并通过ctx.drawImage(textureCanvas, 0, 0) 和WebGL 通过gl.texImage2D 在您的2D 画布中使用它。

标签: javascript html canvas webgl textures


【解决方案1】:

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

如果你有这样的东西会怎样:

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;
}

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

【讨论】:

    猜你喜欢
    • 2020-11-17
    • 2019-05-07
    • 2013-01-05
    • 2012-10-17
    • 2019-01-26
    • 2014-07-24
    • 1970-01-01
    • 2014-04-29
    • 2015-01-28
    相关资源
    最近更新 更多