WebGPU 纹理数组Texture Arrays在大型地形渲染中的应用在大型三维开放世界、航天遥感数字地球或复杂地质可视化中宏大地形表面往往需要混合使用数十种不同的地表材质草地、岩石、泥土、沙滩、积雪与森林。在传统的 WebGL 渲染管线中实现多材质地形混合Texture Splatting面临着严重的硬件瓶颈GPU 的可同时绑定的独立纹理采样器数量极其有限通常仅支持 16 个 Samplers如果采用多个材质分多次渲染Multi-passDraw Call 会随着材质种类成倍暴增如果将所有材质拼成一张巨大的纹理图集Texture Atlas又会在 Mipmap 采样和 UV 边缘产生极其难看的接缝与溢出走样Texture Bleeding。WebGPU 纹理数组texture_2d_array为大型复杂地形渲染提供了终极解法它允许将数十张相同尺寸和格式的独立纹理层打包进单一连续显存数组中着色器只需一个 BindGroup 即可通过动态数组索引Layer Index实现零接缝、全硬件 Mipmap 的极速多材质混合。纹理数组与传统图集的物理机制对比flowchart TD subgraph Atlas [传统 WebGL 纹理图集 (Texture Atlas)] At1[多张贴图强行拼在单张大图上] -- Bleeding[Mipmap 下级采样跨界溢出产生黑缝!] end subgraph TexArray [WebGPU 纹理数组 (texture_2d_array)] Layer0[Layer 0: 草地纹理 1024x1024 (独立 Mipmap)] Layer1[Layer 1: 巖石纹理 1024x1024 (独立 Mipmap)] Layer2[Layer 2: 积雪纹理 1024x1024 (独立 Mipmap)] Layer0 -- GPUArray[单一 GPU 连续显存动态索引采样 (无任何溢出!)] Layer1 -- GPUArray Layer2 -- GPUArray endWGSL 纹理数组地形材质混合着色器实战在 WGSL 片元着色器中我们使用textureSample并传入第三个维度作为材质层索引group(0) binding(0) var terrainSampler: sampler; group(0) binding(1) var terrainTextureArray: texture_2d_arrayf32; // 声明 2D 纹理数组 group(0) binding(2) var splatMap: texture_2df32; // 材质权重分配图 (Splat Map) struct FragmentInput { location(0) uv: vec2f32, location(1) worldPos: vec3f32, location(2) normal: vec3f32, }; fragment fn fs_terrain_main(input: FragmentInput) - location(0) vec4f32 { // 1. 从权重分配图中采样各材质的混合权重 (RGBA 分别对应 4 个地表材质层) let splatWeights textureSample(splatMap, terrainSampler, input.uv); // 2. 地形 UV 细节平铺缩放 let detailUv input.uv * 32.0; // 3. 动态采样 2D 纹理数组中的各个材质层 let grassColor textureSample(terrainTextureArray, terrainSampler, detailUv, 0); // Layer 0: 草地 let rockColor textureSample(terrainTextureArray, terrainSampler, detailUv, 1); // Layer 1: 岩石 let sandColor textureSample(terrainTextureArray, terrainSampler, detailUv, 2); // Layer 2: 沙地 let snowColor textureSample(terrainTextureArray, terrainSampler, detailUv, 3); // Layer 3: 积雪 // 4. 基于 Splat 权重进行线性加权混合 var blendedColor grassColor * splatWeights.r rockColor * splatWeights.g sandColor * splatWeights.b snowColor * splatWeights.a; // 5. 结合光照计算 (简单 Lambertian 漫反射) let lightDir normalize(vec3f32(0.4, 0.8, 0.3)); let diffuse max(dot(normalize(input.normal), lightDir), 0.15); return vec4f32(blendedColor.rgb * diffuse, 1.0); }TypeScript 宿主端纹理数组创建与显存直传export class TerrainTextureArrayManager { private device: GPUDevice; public textureArray!: GPUTexture; constructor(device: GPUDevice) { this.device device; } // 创建并上传 4 层的 1024x1024 纹理数组 async loadTerrainLayers(imageBitmaps: ImageBitmap[]) { const layerCount imageBitmaps.length; const width 1024; const height 1024; // 1. 分配具有 depthOrArrayLayers 维度的 GPU 纹理 this.textureArray this.device.createTexture({ size: { width, height, depthOrArrayLayers: layerCount, // 声明数组层数 }, format: rgba8unorm, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, }); // 2. 逐层将图片位图写入对应的 Layer 显存切片 for (let layer 0; layer layerCount; layer) { this.device.queue.copyExternalImageToTexture( { source: imageBitmaps[layer] }, { texture: this.textureArray, origin: { x: 0, y: 0, z: layer }, // 关键指定目标 z 轴切片层索引 }, [width, height] ); } console.log(✅ 成功将 ${layerCount} 层高精地表材质打包送入 WebGPU 纹理数组); } }性能收益采用纹理数组架构后Draw Call 恒定为 1无论地表混合多少种材质整片万米山川只需单次 Pass 绘制完毕硬件 Mipmap 完美隔离每层纹理拥有独立完整的 Mipmap 链条远景漫游绝对无任何黑线锯齿。用现代 WebGPU 的高维显存架构打破传统约束让辽阔数字山河在屏幕上磅礴舒展。
