首页
/ three.js 中 CompressedCubeTexture 详解:压缩立方体贴图的构造、参数与加载流程

three.js 中 CompressedCubeTexture 详解:压缩立方体贴图的构造、参数与加载流程

2026-09-06 15:34:41作者:沈韬淼Beryl

本文以 three.js 官方 API 文档中的 CompressedCubeTexture 页面为主体,结合 src/ 源码与示例工程,系统讲解这个类的作用、继承关系、构造参数与属性、压缩纹理在 three.js 中的通用约束(flipY、mipmap 生成),以及它如何经由 CompressedTextureLoader / DDSLoader 从 DDS 文件加载为立方体环境贴图。读完本文,你将能够正确手动构造一个压缩立方体贴图、理解其六面数据结构,并复现仓库中 webgl_loader_texture_dds 示例的加载流程。

一、什么是 CompressedCubeTexture

CompressedCubeTexture 用于创建基于压缩格式数据的立方体(cube)纹理。文档页面(docs/pages/CompressedCubeTexture.html.md)给出的原始定义为:

Creates a cube texture based on data in compressed form. These textures are usually loaded with CompressedTextureLoader.

它的典型使用场景是把 DXT1/DXT3/DXT5/BC 系列等 GPU 压缩块纹理(通常存放在 .dds 文件中)作为环境反射、天空盒或法线贴图使用。相比普通 CubeTexture(接收 6 张 Image/Canvas),CompressedCubeTexture 的每一面都是 CompressedTexture 数据,可直接被 GPU 以压缩块格式采样,显存占用与带宽显著低于未压缩 RGBA 数据。

二、继承关系与源码结构

文档明确标注了继承链:

EventDispatcher → Texture → CompressedTexture → CompressedCubeTexture

这一链条可在源码中逐层印证:

该类的公共导出入口在 src/Three.Core.jsexport { CompressedCubeTexture } from './textures/CompressedCubeTexture.js';),因此通过标准 three 包即可直接使用,无需从 examples 附加模块导入。

2.1 构造函数逐行解读

文档给出的构造函数签名为:

new CompressedCubeTexture( images : Array.<CompressedTexture>, format : number, type : number )

对应 src/textures/CompressedCubeTexture.js 的实现:

constructor( images, format, type ) {

    super( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping );

    this.isCompressedCubeTexture = true;
    this.isCubeTexture = true;

    this.image = images;

}

从源码结构看,有三个值得注意的细节:

  1. 尺寸取自第一张图:父类 CompressedTexture 的构造函数会把 image 置为 { width, height } 占位对象(压缩纹理没有可直接显示的位图,image 仅用于描述尺寸),因此 CompressedCubeTextureimages[0].width / images[0].height 作为基准尺寸传入 super(...)
  2. 映射方式硬编码为立方体反射super 调用的最后一个参数是 CubeReflectionMapping(来自 src/constants.js),意味着该纹理天然按立方体贴图方式参与材质反射计算,无需再手动设置 mapping
  3. image 被覆盖为六面数组:构造末尾 this.image = images,把 6 个面的压缩纹理数组挂回 image 属性。渲染端(如 src/renderers/common/SampledTexture.js(CubeTexture | CompressedCubeTexture) 的绑定分支)正是依赖这一约定来识别并绑定立方体纹理。

2.2 参数说明

参数 类型 说明 默认值
images Array<CompressedTexture> 6 张压缩纹理的数组,每个元素对应立方体的一个面,需包含 widthheight 及压缩块 mipmaps 数据 必填
format number 纹理格式(如 RGBAFormat 或具体压缩格式常量) RGBAFormat
type number 纹理数据类型 UnsignedByteType

手动构造示例(假设 6 个面数据来自已解析的压缩块):

import * as THREE from 'three';

// faces[i] 形如 { width, height, format, mipmaps: [ { data, width, height }, ... ] }
const texture = new THREE.CompressedCubeTexture(
    faces,
    THREE.RGBAFormat,        // format
    THREE.UnsignedByteType    // type
);
// texture.mapping 已自动为 CubeReflectionMapping
material.map = texture;

2.3 属性说明

文档列出两个只读布尔属性,均用于类型判断(type testing):

  • .isCompressedCubeTexture : boolean (readonly),默认 true
  • .isCubeTexture : boolean (readonly),默认 true

两者在 src/textures/CompressedCubeTexture.js 中分别赋值为 trueisCubeTexture 与父类 CompressedTexture 上的 isCompressedTexture(见 src/textures/CompressedTexture.js)叠加后,可以用 texture.isCompressedTexture && texture.isCubeTexture 这类组合判断精确区分“压缩 + 立方体”纹理。

三、压缩纹理的通用约束:flipY 与 mipmap

理解 CompressedCubeTexture 必须理解其父类 CompressedTexture 强加的两条限制(src/textures/CompressedTexture.js):

  1. flipY 恒为 false:注释写明“压缩纹理无法在上传 GPU 时沿垂直轴翻转”,因此在 CompressedTexture 中被强制覆盖为 false。这意味着数据朝向完全由压缩文件本身决定;
  2. generateMipmaps 恒为 false:GPU 无法对压缩块数据现场生成 mipmap,mip 链必须内嵌在压缩纹理文件中。如果文件只带基础 mip(mipmapCount === 1),加载流程会相应调整最小过滤方式(见下一节)。

这两条约束解释了为什么 CompressedCubeTextureimages 参数里每个面都必须自带完整的 mipmaps 数组,而不是像普通 CubeTexture 那样传入图像后由 three.js 代为生成。

四、加载流程:CompressedCubeTexture 与 CompressedTextureLoader 的协作

文档提示“这些纹理通常用 CompressedTextureLoader 加载”。基类 src/loaders/CompressedTextureLoader.js 是一个抽象类,负责统一的加载骨架,并把格式解析交给派生类(如 examples/jsm 下的 DDSLoaderKTXLoader 等)实现的 parse() 方法。

load() 方法(src/loaders/CompressedTextureLoader.js)支持两种 URL 形式:

  1. URL 数组(6 个面文件):循环调用 loadTexture(i),每解析出一个面就填入 images[i] = { width, height, format, mipmaps };当 6 个面全部完成(loaded === 6)时,把 texture.image = images、设置 format 并置 needsUpdate = true,触发回调;
  2. 单个 DDS 文件内含完整立方体parse() 返回 isCubemap === true 时,按 mipmaps.length / mipmapCount 算出面数,把扁平的 mipmap 数组按面重组为 6 个 images[f] = { mipmaps, format, width, height } 子对象,同样写回 texture.image

两种路径都有一条关键分支:

if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;

即无 mip 链的压缩纹理会自动退化为 LinearFilter,避免使用依赖 mipmap 的过滤模式导致采样异常——这正是第三节 generateMipmaps = false 约束在加载端的落地体现。

DDS 格式的立方体判定在 examples/jsm/loaders/DDSLoader.js 中实现:解析 DDS 头部的 DDSCAPS2 标志位(dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP),并在后续按 faces = 6 展开各面的数据(约 L319-L339)。

五、仓库内实操示例:webgl_loader_texture_dds

仓库自带完整可运行的示例 examples/webgl_loader_texture_dds.html,演示了单张纹理与立方体贴图两条加载路径:

import { DDSLoader } from 'three/addons/loaders/DDSLoader.js';

const loader = new DDSLoader();

// 普通压缩纹理(2D)
const map = loader.load( 'textures/compressed/disturb_dxt1_mip.dds' );

// 压缩立方体(单文件内含 6 面)
const cubemap = loader.load( 'textures/compressed/Mountains.dds', function ( texture ) {
    // texture.image 为 6 个面的数组,可直接用作 envMap 等
} );

示例中同时覆盖了 disturb_dxt1_*hepatica_dxt3_mip.ddsexplosion_dxt5_mip.ddsdisturb_dx10_bc6h_* 等多种压缩格式,以及带 mip 与不带 mip(nomip)的两类文件;测试素材位于 examples/textures/compressed/ 目录。该示例展示了从加载到赋给材质的完整闭环,是验证 CompressedCubeTexture 相关行为(过滤方式、六面结构)最直接的参照。

六、小结与使用要点

  • CompressedCubeTextureTexture → CompressedTexture 继承链上专门承载“6 面压缩数据”的节点,image 属性即 6 个面的数组,mapping 自动为 CubeReflectionMapping(见 src/textures/CompressedCubeTexture.js);
  • 构造参数为 images(必填的 6 面 CompressedTexture 数组)、format(默认 RGBAFormat)、type(默认 UnsignedByteType);类型判断属性为 isCompressedCubeTextureisCubeTexture,均默认 true
  • 压缩纹理不可 flipY、不可现场生成 mipmap,mip 链必须内嵌于文件;单 mip 文件加载时 minFilter 会自动回退为 LinearFilter(见 src/loaders/CompressedTextureLoader.js);
  • 实际项目中优先通过 CompressedTextureLoader 派生类(如 DDSLoader)加载,再按 examples/webgl_loader_texture_dds.html 的方式将纹理交给材质使用。
登录后查看全文
热门项目推荐
相关项目推荐