如何通过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
相关产品推荐
相关产品推荐

