You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过twgl.js动态更新WebGL中的Texture2DArray?

嘿,我之前在做WebGL纹理数组的时候也踩过这个坑,太懂这种蓝色方块的崩溃感了!咱们来拆解问题、解决它:

问题根源

你猜的完全没错——twgl.createTexture每次调用都会生成一个全新的WebGLTexture对象,而你的渲染逻辑大概率还在引用旧的纹理ID。WebGL在绑定无效/已丢弃的纹理时,会默认渲染蓝色,这就是你看到的现象;手动重新渲染时可能重新绑定了新纹理,所以显示正常,但下次调用createTexture又会重复这个问题。

正确解决方案:更新已有纹理数组

不用每次创建新纹理,而是用twgl提供的setTexturedata方法(或WebGL原生API)直接更新现有TEXTURE_2D_ARRAY的指定层级。具体步骤如下:

1. 初始化足够容量的纹理数组

首先创建一个预设了足够层级数的纹理数组(WebGL2中TEXTURE_2D_ARRAY的层级数量创建后无法修改,所以尽量预估足够的容量,后续不够再扩容):

const gl = /* 你的WebGL2上下文 */;
// 假设每张图尺寸统一为512x512,先创建能容纳10张图的纹理数组
const textureArray = twgl.createTexture(gl, {
  target: gl.TEXTURE_2D_ARRAY,
  internalFormat: gl.RGBA8,
  width: 512,
  height: 512,
  depth: 10, // 最大可存储的图片数量
  minMag: gl.LINEAR,
  wrap: gl.CLAMP_TO_EDGE,
});

2. 动态新增图片时更新纹理层级

当你拿到新的Base64字符串或图片对象时,先确保图片加载完成,再用setTexturedata更新对应层级:

// 处理Base64字符串转为图片对象示例
function addImageToTextureArray(base64Str, layerIndex) {
  const img = new Image();
  img.src = base64Str;
  img.onload = () => {
    // 确保图片尺寸和纹理数组一致,不一致的话先缩放
    twgl.setTextureData(gl, textureArray, {
      target: gl.TEXTURE_2D_ARRAY,
      data: img,
      level: 0, // mipmap层级,一般用0即可
      zoffset: layerIndex, // 要更新的纹理数组层级索引(从0开始)
      width: img.width,
      height: img.height,
      depth: 1, // 只更新1个层级
    });
    // 这里可以触发一次渲染,确保更新立刻生效
    render();
  };
}

3. 若纹理数组容量不足:扩容并迁移数据

如果预设的层级不够用了,只能创建新的更大的纹理数组,再把旧数据复制过去:

function resizeTextureArray(oldTexture, newDepth) {
  const { width, height } = twgl.getTextureInfo(gl, oldTexture);
  // 创建新的更大容量的纹理数组
  const newTexture = twgl.createTexture(gl, {
    target: gl.TEXTURE_2D_ARRAY,
    internalFormat: gl.RGBA8,
    width,
    height,
    depth: newDepth,
    minMag: gl.LINEAR,
    wrap: gl.CLAMP_TO_EDGE,
  });
  // 复制旧纹理的所有层级到新纹理
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, newTexture);
  gl.copyImageSubData(
    oldTexture, gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0,
    newTexture, gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0,
    width, height, twgl.getTextureInfo(gl, oldTexture).depth
  );
  // 删除旧纹理避免内存泄漏
  gl.deleteTexture(oldTexture);
  return newTexture;
}

// 使用示例:把原纹理从10层级扩容到20层级
textureArray = resizeTextureArray(textureArray, 20);

关键注意点

  • 所有图片必须和纹理数组的width/height一致,否则上传会失败;如果图片尺寸不统一,要先通过Canvas缩放后再上传。
  • 必须等待图片加载完成(img.onload触发)再更新纹理,否则空数据会导致蓝色方块。
  • 更新纹理后最好触发一次渲染,确保GPU能及时拿到新数据。

内容的提问来源于stack exchange,提问作者mh-alahdadian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:12:51