DirectX 11中如何调整Texture2DArray大小以存储新增平行光阴影贴图
Texture2DArray动态扩容存储平行光阴影贴图的实现方案
你原有方案的核心问题是冗余维护了大量独立Texture2D,且每次重建数组都要执行多次纹理拷贝操作,效率和可维护性都偏低,以下是更优的落地思路:
方案1:预分配冗余容量(首选方案,绝大多数场景适用)
- 初始化时不要按照当前实际灯光数量创建数组,而是提前分配一个足够容纳预期最大灯光数的容量,建议取值为2的幂(如8、16、32层),普通游戏场景中平行光数量通常不会超过32个,预分配的显存开销完全可控
- 维护一个全局的
当前生效平行光数量变量,作为Uniform传入着色器,采样时如果灯光索引超过该值,直接跳过阴影计算即可,未使用的数组层不会参与实际渲染,也不会产生额外性能开销 - 只有当实际灯光数量超过预分配最大容量时,才需要执行一次扩容操作,大幅降低重建数组的频率
方案2:GPU直拷扩容(适用于需要频繁调整灯光数量的场景)
如果确实需要动态扩容且不想浪费冗余显存,可以优化扩容逻辑,完全避免维护独立Texture2D:
- 每次扩容时直接创建更大的新Texture2DArray,保持分辨率、纹理格式、采样参数和旧数组完全一致,建议每次按2倍容量扩容,进一步减少扩容次数
- 调用图形API的GPU纹理拷贝指令(不同框架/API对应接口如下),直接把旧数组的所有层一次性拷贝到新数组的前N层,整个过程完全走GPU显存带宽,耗时远低于CPU侧中转或逐张拷贝独立纹理
- Unity:
Graphics.CopyTexture - Unreal Engine:
FRHICommandList::CopyTexture - OpenGL:
glCopyImageSubData - DirectX 11/12:
CopySubresourceRegion - Vulkan:
vkCmdCopyImage
- Unity:
- 拷贝完成后直接释放旧数组即可,无需维护额外的独立纹理资源
Unity环境示例代码
// 阴影贴图数组扩容方法 public void ResizeShadowmapArray(ref Texture2DArray existingArray, int requiredLayerCount) { // 容量足够无需扩容 if (existingArray != null && requiredLayerCount <= existingArray.depth) return; int newLayerCount = Mathf.NextPowerOfTwo(requiredLayerCount); Texture2DArray newArray = new Texture2DArray( width: existingArray?.width ?? 2048, // 阴影贴图分辨率可自行配置 height: existingArray?.height ?? 2048, depth: newLayerCount, format: existingArray?.format ?? TextureFormat.Shadowmap, mipCount: existingArray?.mipmapCount ?? 1, linear: true ); newArray.filterMode = FilterMode.Point; newArray.wrapMode = TextureWrapMode.Clamp; // 拷贝旧数组内容到新数组 if (existingArray != null) { Graphics.CopyTexture(existingArray, 0, existingArray.depth, newArray, 0, 0); Object.Destroy(existingArray); } existingArray = newArray; }
注意事项
- 扩容操作建议放在帧间隙或者加载阶段执行,避免在渲染密集期触发产生卡顿
- 着色器侧原有按灯光索引采样的逻辑完全不需要修改,仅需新增一个Uniform控制生效灯光数量即可,完全保留你选择Texture2DArray的便捷性优势
内容的提问来源于stack exchange,提问作者Gregor Sattel
相关产品推荐
相关产品推荐

