跳到主要内容

纹理

纹理是存储在 GPU 上的图像或数据网格。通常作为资源加载并由材质使用,也可在代码中创建、填入数据、在自定义着色器中采样,或通过渲染目标向其渲染。

纹理类型​

类型创建选项典型用途
二维纹理默认图像、数据贴图、渲染目标输出
立方体贴图cubemap: true环境贴图、反射、点光源阴影,包含六个方形面
二维纹理数组arrayLength: n相同尺寸的二维层堆栈,通过层索引采样,用于地形层、替身和精灵集
体纹理(三维)volume: true 和 depth: n以三维坐标采样的纹素网格,用于体积雾、云、三维查找表和体素数据

创建纹理​

const texture = new Texture(app.graphicsDevice, {
name: 'MyTexture',
width: 256,
height: 256,
format: PIXELFORMAT_RGBA8,
mipmaps: true,
minFilter: FILTER_LINEAR_MIPMAP_LINEAR,
magFilter: FILTER_LINEAR,
addressU: ADDRESS_CLAMP_TO_EDGE,
addressV: ADDRESS_CLAMP_TO_EDGE
});

其他纹理类型使用相同构造函数,通过选项选择:

// a cubemap with six 128x128 faces
const cubemap = new Texture(app.graphicsDevice, { width: 128, height: 128, cubemap: true });

// a texture array with 16 layers of 256x256
const textureArray = new Texture(app.graphicsDevice, { width: 256, height: 256, arrayLength: 16 });

// a volume texture of 64x64x32 texels
const volume = new Texture(app.graphicsDevice, { width: 64, height: 64, depth: 32, volume: true });

像素格式​

format 决定各纹素如何存储。Texture API 参考列出了所有格式及 WebGL2、WebGPU 支持细节。主要分类为:

  • 8 位格式:默认 PIXELFORMAT_RGBA8,以及 PIXELFORMAT_RG8、PIXELFORMAT_R8,用于颜色和一般数据。
  • sRGB 格式:多数颜色图像等在 sRGB 中制作的纹理,应使用 PIXELFORMAT_SRGBA8 等 sRGB 格式,或用 srgb: true 选择格式的 sRGB 变体。采样时转换到线性空间,保证光照符合物理规律。参见线性工作流和 sRGB 纹理处理。
  • HDR 格式:半浮点 PIXELFORMAT_RGBA16F、浮点 PIXELFORMAT_RGBA32F、小浮点 PIXELFORMAT_RG11B10F 和紧凑共享指数 PIXELFORMAT_RGB9E5,用于 0–1 之外的值。参见 HDR 渲染。
  • 整数格式:例如 PIXELFORMAT_R8U、PIXELFORMAT_RGBA32U,保存整数,不能滤波,需在着色器中按纹素坐标读取。
  • 压缩格式:DXT、ETC、PVRTC、ASTC 等系列,大幅节省内存,通常由纹理资源生成。参见纹理压缩。
  • 深度格式:PIXELFORMAT_DEPTH、PIXELFORMAT_DEPTH16 和 PIXELFORMAT_DEPTHSTENCIL,作为目标的深度缓冲区。

并非所有格式都可用于渲染输出,参见渲染目标页面的选择格式。

上传数据​

创建时可通过 levels 提供各 mip 层级的数据,从最高分辨率层开始:

  • 二维纹理:各层为类型化数组,或图像、画布、ImageBitmap、视频等浏览器图像源。
  • 立方体贴图:各层为六个面的数组,顺序为 +X、-X、+Y、-Y、+Z、-Z。
  • 纹理数组:各 mip 层级为数组,每个数组条目对应一个纹理层。
  • 体纹理:各 mip 层级为单个类型化数组,依次保存全部深度切片。
// a volume texture, filled with data for all its depth slices
const data = new Uint8Array(64 * 64 * 32 * 4);
// ... fill the data, slice after slice, row after row
const volume = new Texture(app.graphicsDevice, {
width: 64,
height: 64,
depth: 32,
volume: true,
format: PIXELFORMAT_RGBA8,
levels: [data]
});

之后也可更新:

  • lock 返回待填充的 mip 层级或立方体面的类型化数组,unlock 上传它。体纹理数组包含该 mip 层级的全部深度切片。
  • setSource 设置画布、视频等浏览器图像源。
const pixels = texture.lock();
for (let i = 0; i < pixels.length; i += 4) {
pixels[i] = 255; // red
pixels[i + 1] = 0; // green
pixels[i + 2] = 0; // blue
pixels[i + 3] = 255; // alpha
}
texture.unlock();

采样选项​

以下选项控制滤波和环绕,之后也可通过同名属性更改:

  • minFilter 和 magFilter:缩小与放大时的滤波,例如三线性的 FILTER_LINEAR_MIPMAP_LINEAR,或不滤波的 FILTER_NEAREST。
  • anisotropy:各向异性滤波级别,保持掠射角纹理清晰。
  • addressU、addressV、addressW:各方向的环绕,例如 ADDRESS_REPEAT 或 ADDRESS_CLAMP_TO_EDGE;W 用于体纹理。

Mipmap​

Mipmap 是经过滤波的较小纹理副本,让远处采样平滑且高效。mipmaps 默认启用,层数由最大维度决定,体纹理也考虑深度。可用 numLevels 显式指定层数。

如果只提供最高分辨率数据,上传时会自动生成其余层级,适用于包括体纹理在内的所有类型。压缩和整数纹理不支持自动生成,需提供全部层级。

向纹理渲染后也会生成 mipmap,参见渲染目标的 Mipmap。

在着色器中采样​

StandardMaterial 等材质采样二维纹理和立方体贴图。纹理数组和体纹理需通过自定义着色器,按匹配类型声明:

纹理类型GLSLWGSL
二维纹理sampler2Dtexture_2d<f32>
立方体贴图samplerCubetexture_cube<f32>
二维纹理数组sampler2DArraytexture_2d_array<f32>
体纹理sampler3Dtexture_3d<f32>

例如纹理数组和体纹理:

uniform mediump sampler2DArray uLayers;
uniform mediump sampler3D uVolume;

// in the fragment shader
vec4 layerColor = texture(uLayers, vec3(uv, layerIndex));
vec4 volumeColor = texture(uVolume, uvw);
material.setParameter('uLayers', textureArray);
material.setParameter('uVolume', volume);

整数纹理使用整数采样类型,例如 GLSL 的 usampler2D、WGSL 的 texture_2d<u32>,通过 texelFetch / textureLoad 按纹素坐标读取。

WebGPU 可通过 Texture#getView 创建 TextureView,只绑定部分 mip 层级或数组层。WebGL 中视图绑定整张纹理。

回读和复制​

Texture#read 从 GPU 下载矩形像素区域,返回解析为这些数据的 Promise。face 选择立方体面,layer 选择数组层,slice 选择体纹理深度切片。省略 slice 时,一次操作依次读取全部切片:

const pixels = await texture.read(0, 0, texture.width, texture.height, { mipLevel: 0 });

// a single depth slice of a volume texture, or all of them
const slicePixels = await volume.read(0, 0, volume.width, volume.height, { slice: 5 });
const volumePixels = await volume.read(0, 0, volume.width, volume.height);

Texture#copy 在 GPU 上将纹理区域复制到相同格式的另一纹理。支持相同的 face、layer、slice,省略 slice 时复制体纹理全部切片:

// copy all depth slices of a volume texture
destination.copy(volume);

// copy one layer of a texture array
destinationArray.copy(textureArray, { layer: 3 });

渲染到纹理​

可以向纹理渲染,包括立方体面、数组层和体纹理深度切片。参见渲染目标。

纹理内存​

纹理销毁前会持续占用 GPU 内存。大小取决于尺寸、格式和 mip 层级;立方体全部面、数组全部层和体纹理全部切片均计入。观察内存可使用:

  • MiniStats 的 VRAM 部分显示纹理 GPU 内存。
  • 检查器的 Textures 标签按大小降序列出设备纹理,Memory 标签按资源类型细分显存。

代码创建的纹理不再使用时,应销毁:

texture.destroy();