threejs-asset-pipeline
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThree.js Asset Pipeline
Three.js 资源管线
Targets three.js r186. Examples use WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer.
以 three.js r186 为目标。示例使用 WebGLRenderer;WebGPU 和 TSL 一节使用 WebGPURenderer。
Quick Start
快速开始
Compress on the command line, then load the result. Run before and after to see what the pass saved:
inspectbash
npx @gltf-transform/cli inspect model.glb
npx @gltf-transform/cli optimize model.glb model.opt.glb --compress draco --texture-compress webp --texture-size 2048
npx @gltf-transform/cli inspect model.opt.glbDraco output needs a DRACOLoader attached; WebP textures decode in the browser:
javascript
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const sun = new THREE.DirectionalLight(0xffffff, 2); // glTF PBR materials render black without lights or an environment
sun.position.set(3, 5, 2);
scene.add(sun, new THREE.HemisphereLight(0xffffff, 0x444444, 1.5));
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(2, 1.5, 3);
camera.lookAt(0, 0, 0);
const gltf = await new GLTFLoader()
.setDRACOLoader(new DRACOLoader()) // the Draco WASM decoder loads from the three package
.loadAsync("model.opt.glb");
scene.add(gltf.scene);
function animate() {
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);先在命令行中压缩,再加载结果。运行 前后对比,看看这一轮处理节省了多少:
inspectbash
npx @gltf-transform/cli inspect model.glb
npx @gltf-transform/cli optimize model.glb model.opt.glb --compress draco --texture-compress webp --texture-size 2048
npx @gltf-transform/cli inspect model.opt.glbDraco 输出需要挂载 DRACOLoader;WebP 纹理可直接在浏览器中解码:
javascript
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const sun = new THREE.DirectionalLight(0xffffff, 2); // glTF PBR materials render black without lights or an environment
sun.position.set(3, 5, 2);
scene.add(sun, new THREE.HemisphereLight(0xffffff, 0x444444, 1.5));
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(2, 1.5, 3);
camera.lookAt(0, 0, 0);
const gltf = await new GLTFLoader()
.setDRACOLoader(new DRACOLoader()) // the Draco WASM decoder loads from the three package
.loadAsync("model.opt.glb");
scene.add(gltf.scene);
function animate() {
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);Inspect and Validate
检查与验证
Never guess where the bytes are. prints an overview (generator, , ) and one table each for scenes, meshes, materials, textures and animations, so a geometry-heavy file and a texture-heavy file are obvious at a glance.
inspectextensionsUsedextensionsRequiredbash
npx @gltf-transform/cli inspect model.glb
npx @gltf-transform/cli inspect model.opt.glb --format csv
npx @gltf-transform/cli validate model.glb --ignore ACCESSOR_WEIGHTS_NON_NORMALIZEDextensionsRequiredKHR_draco_mesh_compressionEXT_meshopt_compressionKHR_texture_basisuKHR_mesh_quantizationEXT_mesh_gpu_instancingEXT_texture_webpEXT_texture_avifvalidateinspect不要凭空猜测体积花在何处。 会打印概览(generator、、),并分别为场景、网格、材质、纹理和动画输出一张表,因此一眼就能看出文件是几何体偏重还是纹理偏重。
inspectextensionsUsedextensionsRequiredbash
npx @gltf-transform/cli inspect model.glb
npx @gltf-transform/cli inspect model.opt.glb --format csv
npx @gltf-transform/cli validate model.glb --ignore ACCESSOR_WEIGHTS_NON_NORMALIZEDextensionsRequiredKHR_draco_mesh_compressionEXT_meshopt_compressionKHR_texture_basisuKHR_mesh_quantizationEXT_mesh_gpu_instancingEXT_texture_webpEXT_texture_avifvalidateThe optimize Command
optimize 命令
optimizeinspectbash
undefinedoptimizeinspectbash
undefineddraco geometry + webp textures, capped at 2048px
draco geometry + webp textures, capped at 2048px
npx @gltf-transform/cli optimize model.glb model.opt.glb --compress draco --texture-compress webp --texture-size 2048
npx @gltf-transform/cli optimize model.glb model.opt.glb --compress draco --texture-compress webp --texture-size 2048
GPU textures instead of download size, keep every triangle
GPU textures instead of download size, keep every triangle
npx @gltf-transform/cli optimize model.glb model.opt.glb --texture-compress ktx2 --simplify false
Main flags (booleans take an explicit value, like `--simplify false`):
- `--compress draco|meshopt|quantize` — geometry and animation compression (default `meshopt`).
- `--texture-compress ktx2|webp|avif` — texture codec (default `auto`, recompress in the original format).
- `--texture-size <px>` — cap texture dimensions, default 2048.
- `--simplify <bool>`, `--simplify-ratio <0-1>`, `--simplify-error <fraction>`, `--simplify-lock-border <bool>` — mesh simplification; ratio 0 and error 0.0001 by default.
- `--instance <bool>`, `--instance-min <count>` — GPU instancing for repeated meshes.
- `--join <bool>`, `--join-meshes <bool>`, `--join-named <bool>`, `--flatten <bool>` — draw-call reduction; `--join` requires `--flatten`.
- `--weld <bool>` — merge duplicate vertices; `draco` welds by itself even with `--weld false`, while `simplify` welds only unindexed primitives, so keep `--weld true` when simplifying.
- `--prune <bool>`, `--resample <bool>`, `--palette <bool>`, `--sparse <bool>`, `--meshopt-level medium|high`.
With `--texture-compress ktx2`, `optimize` picks bitstreams itself: UASTC for the normal, occlusion and metallic-roughness slots, ETC1S for everything else. For alternatives outside this skill's lane, gltfpack covers similar ground; gltf-transform is enough here.npx @gltf-transform/cli optimize model.glb model.opt.glb --texture-compress ktx2 --simplify false
主要参数(布尔参数需要显式指定值,例如 `--simplify false`):
- `--compress draco|meshopt|quantize`——几何体和动画压缩,默认使用 `meshopt`。
- `--texture-compress ktx2|webp|avif`——纹理编解码器,默认值为 `auto`,以原始格式重新压缩。
- `--texture-size <px>`——限制纹理尺寸,默认值为 2048。
- `--simplify <bool>`、`--simplify-ratio <0-1>`、`--simplify-error <fraction>`、`--simplify-lock-border <bool>`——网格简化;ratio 默认为 0,error 默认为 0.0001。
- `--instance <bool>`、`--instance-min <count>`——对重复网格使用 GPU instancing。
- `--join <bool>`、`--join-meshes <bool>`、`--join-named <bool>`、`--flatten <bool>`——减少 draw call;使用 `--join` 时必须同时启用 `--flatten`。
- `--weld <bool>`——合并重复顶点;即使设置 `--weld false`,`draco` 也会自行执行顶点合并,而 `simplify` 只会处理未索引 primitive,因此在简化时应保持 `--weld true`。
- `--prune <bool>`、`--resample <bool>`、`--palette <bool>`、`--sparse <bool>`、`--meshopt-level medium|high`。
使用 `--texture-compress ktx2` 时,`optimize` 会自行选择 bitstream:法线、遮蔽和 metallic-roughness 槽位使用 UASTC,其余使用 ETC1S。对于本 skill 范围之外的替代方案,gltfpack 也覆盖类似场景;这里使用 gltf-transform 已经足够。Geometry Compression
几何体压缩
Three codecs with different tradeoffs — pick per model, not per project:
- Draco (,
--compress dracocommand): compresses triangle-mesh geometry only — animation, morph targets, textures, points and lines are not compressed. Runtime: DRACOLoader.draco - Meshopt (,
--compress meshoptcommand): compresses point, line and triangle geometry, morph targets and animation tracks, and decodes very quickly; pair it with lossless gzip or brotli over the wire. Runtime: MeshoptDecoder.meshopt - Quantize (command): stores float attributes as 8- or 16-bit integers under KHR_mesh_quantization — smaller on disk and in GPU memory, and no decoder to wire; GLTFLoader reads quantized attributes natively.
quantize
bash
npx @gltf-transform/cli draco model.glb model.opt.glb --method edgebreaker --quantize-position 14
npx @gltf-transform/cli meshopt model.glb model.opt.glb --level high
npx @gltf-transform/cli quantize model.glb model.opt.glb
npx @gltf-transform/cli gzip model.opt.glbgzipmodel.opt.glb.gzContent-Encoding: gzipMake , or the last command in a chain: every other command that rewrites the model decodes Draco and meshopt geometry when it reads the file (it warns "Decoded KHR_draco_mesh_compression. Further compression will be lossy.") and writes it back uncompressed.
dracomeshoptoptimize三种 codec 各有取舍——应按模型选择,而不是按项目选择:
- Draco(,
--compress draco命令):仅压缩三角形网格几何体——不会压缩动画、morph target、纹理、点和线。运行时需要 DRACOLoader。draco - Meshopt(,
--compress meshopt命令):可压缩点、线和三角形几何体、morph target 以及动画轨道,解码速度非常快;可通过网络传输搭配无损 gzip 或 brotli。运行时需要 MeshoptDecoder。meshopt - Quantize(命令):按照 KHR_mesh_quantization 将浮点属性存储为 8 位或 16 位整数——在磁盘和 GPU 内存中的占用更小,并且无需配置解码器;GLTFLoader 可原生读取量化属性。
quantize
bash
npx @gltf-transform/cli draco model.glb model.opt.glb --method edgebreaker --quantize-position 14
npx @gltf-transform/cli meshopt model.glb model.opt.glb --level high
npx @gltf-transform/cli quantize model.glb model.opt.glb
npx @gltf-transform/cli gzip model.opt.glbgzipmodel.opt.glb.gzContent-Encoding: gzip务必将 、 或 放在命令链的最后:其他所有会重写模型的命令在读取文件时都会解码 Draco 和 meshopt 几何体,并警告 ,随后以未压缩形式写回。
dracomeshoptoptimizeDecoded KHR_draco_mesh_compression. Further compression will be lossy.Texture Compression
纹理压缩
Two different problems: KTX2 (Basis Universal) stays compressed in GPU memory wherever the GPU supports one of its transcode targets (KTX2Loader falls back to uncompressed RGBA otherwise) and uploads fast — it optimizes GPU memory and performance — while WebP and AVIF shrink the download and are decompressed to raw pixels in GPU memory, which can occupy 4-8x more there.
bash
undefined这里涉及两个不同问题:KTX2(Basis Universal)在 GPU 支持其任一转码目标时,会以压缩形式保留在 GPU 内存中(否则 KTX2Loader 会回退到未压缩的 RGBA),并且上传速度很快——它优化的是 GPU 内存和性能;而 WebP 与 AVIF 缩减的是下载体积,解压后在 GPU 内存中会变为原始像素,占用可能达到 4 至 8 倍。
bash
undefinedKTX2: ETC1S for color (smaller, lossier), UASTC for normal maps (larger, cleaner)
KTX2: ETC1S for color (smaller, lossier), UASTC for normal maps (larger, cleaner)
npx @gltf-transform/cli etc1s model.glb model.opt.glb --quality 128
npx @gltf-transform/cli uastc model.glb model.opt.glb --level 4
npx @gltf-transform/cli etc1s model.glb model.opt.glb --quality 128
npx @gltf-transform/cli uastc model.glb model.opt.glb --level 4
Download size: WebP or AVIF, quality 1-100
Download size: WebP or AVIF, quality 1-100
npx @gltf-transform/cli webp model.glb model.opt.glb --quality 80
npx @gltf-transform/cli webp model.glb model.opt.glb --quality 80
Cap dimensions of PNG or JPEG textures; never enlarges them
Cap dimensions of PNG or JPEG textures; never enlarges them
npx @gltf-transform/cli resize model.glb model.opt.glb --width 1024 --height 1024
KTX2 output — `etc1s`, `uastc` or `optimize --texture-compress ktx2` — shells out to the `ktx` command from KTX-Software (4.4.0 or newer), so install that first; WebP and AVIF are encoded in-process. The single `etc1s` and `uastc` commands convert every texture to one bitstream; the per-slot split (UASTC for normal, occlusion and metallic-roughness, ETC1S for the rest) is what `optimize --texture-compress ktx2` does for you, and `--slots` selects texture roles by name when you need a custom split. Runtime: KTX2 needs KTX2Loader with `detectSupport`; WebP and AVIF decode in the browser.npx @gltf-transform/cli resize model.glb model.opt.glb --width 1024 --height 1024
KTX2 输出——包括 `etc1s`、`uastc` 或 `optimize --texture-compress ktx2`——会调用 KTX-Software 提供的 `ktx` 命令(需 4.4.0 或更高版本),因此必须先安装它;WebP 和 AVIF 则在进程内编码。单独的 `etc1s` 和 `uastc` 命令会将所有纹理转换为一种 bitstream;`optimize --texture-compress ktx2` 会按槽位自动拆分:法线、遮蔽和 metallic-roughness 使用 UASTC,其余使用 ETC1S。需要自定义拆分时,可使用 `--slots` 按名称选择纹理角色。运行时,KTX2 需要带 `detectSupport` 的 KTX2Loader;WebP 和 AVIF 可直接在浏览器中解码。Scene Optimization Commands
场景优化命令
Run these when shows the problem is draw calls or vertex count rather than bytes:
inspectbash
npx @gltf-transform/cli weld model.glb model.opt.glb
npx @gltf-transform/cli simplify model.glb model.opt.glb --ratio 0.5 --error 0.001
npx @gltf-transform/cli join model.glb model.opt.glb
npx @gltf-transform/cli instance model.glb model.opt.glb --min 5
npx @gltf-transform/cli dedup model.glb model.opt.glb
npx @gltf-transform/cli prune model.glb model.opt.glb- merges bitwise-identical vertices and indexes the primitive (points are skipped).
weldwelds by itself;dracowelds only unindexed primitives andsimplifynone, and meshopt's reorder step skips unindexed primitives (it warns when none are indexed), so runmeshoptbeforeweldandsimplify.meshopt - removes triangles, aiming for
simplify(fraction of vertices to keep) while staying under--ratio(fraction of mesh extent);--errorpreserves open edges. It is lossy.--lock-border true - merges compatible primitives to cut draw calls; it runs dedup and flatten itself first. In scenes that reuse one mesh heavily it inflates vertex count — use
jointhere instead.instance - writes EXT_mesh_gpu_instancing for meshes referenced by several nodes (
instancedefaults to 5); GLTFLoader renders those batches as an InstancedMesh with no runtime setup. It skips any file that contains an animation, and instanced meshes must share materials.--min - merges redundant accessors, materials, meshes, skins and textures left by exporters.
dedup - deletes properties no Scene references, including animations that target nothing attached to a scene.
prune
当 显示问题在于 draw call 或顶点数,而非文件字节数时,运行以下命令:
inspectbash
npx @gltf-transform/cli weld model.glb model.opt.glb
npx @gltf-transform/cli simplify model.glb model.opt.glb --ratio 0.5 --error 0.001
npx @gltf-transform/cli join model.glb model.opt.glb
npx @gltf-transform/cli instance model.glb model.opt.glb --min 5
npx @gltf-transform/cli dedup model.glb model.opt.glb
npx @gltf-transform/cli prune model.glb model.opt.glb- 合并逐位完全相同的顶点,并将 primitive 转换为索引形式(会跳过点)。
weld会自行执行顶点合并;draco只会处理未索引 primitive,simplify则不会处理,而且 meshopt 的重排步骤会跳过未索引 primitive(未找到索引 primitive 时会发出警告),因此应在meshopt和simplify之前运行meshopt。weld - 通过移除三角形进行简化,目标是保留
simplify指定比例的顶点,同时将误差控制在--ratio指定的网格范围比例以内;--error可保留开放边缘。此过程有损。--lock-border true - 合并兼容的 primitive,以减少 draw call;它会先自行执行 dedup 和 flatten。在大量重复使用同一个网格的场景中,这可能增加顶点数,此时应改用
join。instance - 会为被多个节点引用的网格写入 EXT_mesh_gpu_instancing(
instance默认值为 5);GLTFLoader 会将这些批次渲染为 InstancedMesh,无需运行时设置。包含动画的文件会被跳过,并且实例化网格必须共用材质。--min - 合并导出器遗留的冗余 accessor、材质、网格、蒙皮和纹理。
dedup - 删除 Scene 未引用的所有属性,包括目标未附加到 Scene 的动画。
prune
Loader Setup for Each Output
每种输出的加载器配置
The pipeline choice decides the loader wiring. The Draco decoder and the Basis transcoder load from the three package by default — call or only when you host those files yourself, and then pin the path to the three release you ship. Create one KTX2Loader for the app and share it between GLTFLoaders: each active instance loads its own transcoder and workers, and three.js warns when several are active.
setDecoderPathsetTranscoderPathjavascript
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
import { MeshoptDecoder } from "three/addons/libs/meshopt_decoder.module.js";
// Wire every codec the pipeline may emit; a missing one throws at load time
const gltfLoader = new GLTFLoader()
.setDRACOLoader(new DRACOLoader()) // KHR_draco_mesh_compression
.setMeshoptDecoder(MeshoptDecoder) // EXT_meshopt_compression
.setKTX2Loader(new KTX2Loader().detectSupport(renderer)); // KHR_texture_basisu
const gltf = await gltfLoader.loadAsync("model.opt.glb");
scene.add(gltf.scene);What each missing piece throws: a Draco file without a DRACOLoader ("No DRACOLoader instance provided"), a meshopt file without a decoder ("setMeshoptDecoder must be called before loading compressed files"), a KTX2 file without a KTX2Loader ("setKTX2Loader must be called before loading KTX2 textures"), and a KTX2Loader that never ran ("Missing initialization with "). WebP and AVIF textures, quantized attributes and EXT_mesh_gpu_instancing need none of this.
detectSupport.detectSupport( renderer )管线方案决定加载器的配置方式。Draco decoder 和 Basis transcoder 默认从 three 包加载——仅当自行托管这些文件时,才调用 或 ,并将路径固定到所使用的 three 版本。为应用创建一个 KTX2Loader,并在多个 GLTFLoader 之间共享:每个活跃实例都会加载自己的 transcoder 和 worker,多个实例同时处于活跃状态时 three.js 会发出警告。
setDecoderPathsetTranscoderPathjavascript
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
import { MeshoptDecoder } from "three/addons/libs/meshopt_decoder.module.js";
// Wire every codec the pipeline may emit; a missing one throws at load time
const gltfLoader = new GLTFLoader()
.setDRACOLoader(new DRACOLoader()) // KHR_draco_mesh_compression
.setMeshoptDecoder(MeshoptDecoder) // EXT_meshopt_compression
.setKTX2Loader(new KTX2Loader().detectSupport(renderer)); // KHR_texture_basisu
const gltf = await gltfLoader.loadAsync("model.opt.glb");
scene.add(gltf.scene);缺少相应组件时分别会抛出以下错误:Draco 文件未配置 DRACOLoader 会报 ;meshopt 文件未配置 decoder 会报 ;KTX2 文件未配置 KTX2Loader 会报 ;KTX2Loader 未运行 会报 。WebP 和 AVIF 纹理、量化属性以及 EXT_mesh_gpu_instancing 均不需要这些配置。
No DRACOLoader instance providedsetMeshoptDecoder must be called before loading compressed filessetKTX2Loader must be called before loading KTX2 texturesdetectSupportMissing initialization with .detectSupport( renderer )Scripted Pipelines
脚本化管线
When the CLI stops fitting — per-project defaults, CI, many files — script the same transforms with the SDK. A Node script, run with :
node pipeline.mjsjavascript
// deps: @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions draco3dgltf
import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { dedup, prune, weld, draco } from "@gltf-transform/functions";
import draco3d from "draco3dgltf";
const io = new NodeIO()
.registerExtensions(ALL_EXTENSIONS) // an extension left out is dropped on read, or throws if required
.registerDependencies({
"draco3d.decoder": await draco3d.createDecoderModule(), // reads Draco input
"draco3d.encoder": await draco3d.createEncoderModule(),
});
const document = await io.read("model.glb");
await document.transform(
dedup(),
weld(), // shared vertices, before compression
prune(), // drop what no Scene references
draco({ method: "edgebreaker", quantizePosition: 14 }),
);
await io.write("model.opt.glb", document);Compression runs when the I/O class writes the file, so it stays the last step. Register every extension: the I/O writes only registered extensions, so WebP textures or quantized attributes without theirs make an invalid file. For meshopt instead, add to after (MeshoptEncoder from the meshoptimizer package) and pass ; texture compression in scripts takes with sharp as the encoder.
"meshopt.encoder": MeshoptEncoderregisterDependenciesawait MeshoptEncoder.readymeshopt({ encoder: MeshoptEncoder })textureCompress({ encoder, targetFormat: "webp" })当 CLI 不再适用时——例如需要项目级默认配置、CI 或处理大量文件——可以使用 SDK 编排相同的转换。通过 运行以下 Node 脚本:
node pipeline.mjsjavascript
// deps: @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions draco3dgltf
import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { dedup, prune, weld, draco } from "@gltf-transform/functions";
import draco3d from "draco3dgltf";
const io = new NodeIO()
.registerExtensions(ALL_EXTENSIONS) // an extension left out is dropped on read, or throws if required
.registerDependencies({
"draco3d.decoder": await draco3d.createDecoderModule(), // reads Draco input
"draco3d.encoder": await draco3d.createEncoderModule(),
});
const document = await io.read("model.glb");
await document.transform(
dedup(),
weld(), // shared vertices, before compression
prune(), // drop what no Scene references
draco({ method: "edgebreaker", quantizePosition: 14 }),
);
await io.write("model.opt.glb", document);压缩会在 I/O 类写入文件时执行,因此必须保留为最后一步。需要注册所有 extension:I/O 只会写入已注册的 extension,因此 WebP 纹理或量化属性如果没有对应 extension,就会生成无效文件。如果改用 meshopt,则应在 之后,将 添加到 (MeshoptEncoder 来自 meshoptimizer 包),并传入 ;脚本中的纹理压缩使用 ,并以 sharp 作为 encoder。
await MeshoptEncoder.ready"meshopt.encoder": MeshoptEncoderregisterDependenciesmeshopt({ encoder: MeshoptEncoder })textureCompress({ encoder, targetFormat: "webp" })React Output with gltfjsx
使用 gltfjsx 生成 React 输出
gltfjsxbash
npx gltfjsx model.glb --transformThis writes (a component for @react-three/fiber; for a .tsx) and, because of , also rewrites the asset: gets palette materials, joined meshes, welded and pruned geometry, Draco compression, and textures re-encoded as WebP, capped at (1024 by default) — normal maps are re-encoded as JPEG, capped at 2048 px or , whichever is larger — and the generated component loads that file. Add with and for simplification. The component side (Canvas, Suspense, useGLTF) is the threejs-react-three-fiber skill.
Model.jsx--types--transformmodel-transformed.glb--resolution--resolution--simplify--ratio--errorgltfjsxbash
npx gltfjsx model.glb --transform该命令会写出 (用于 @react-three/fiber 的组件;使用 可生成 .tsx)。由于启用了 ,它还会重写资源: 将包含调色板材质、合并后的网格、经过顶点合并和清理的几何体、Draco 压缩,以及重新编码为 WebP 的纹理;纹理尺寸限制为 ,默认值为 1024——法线贴图会重新编码为 JPEG,尺寸上限为 2048 px 或 中的较大者——生成的组件也会加载该文件。添加 并配合 和 ,即可执行简化。组件侧内容(Canvas、Suspense、useGLTF)由 threejs-react-three-fiber skill 负责。
Model.jsx--types--transformmodel-transformed.glb--resolution--resolution--simplify--ratio--errorWebGPU and TSL
WebGPU 与 TSL
The loaders and addons above are identical under WebGPURenderer, which renders the loaded materials as node materials by itself ( stays the class GLTFLoader created). One ordering rule: needs the renderer's backend, so create the loader after .
mesh.materialKTX2Loader.detectSupport(renderer)await renderer.init()javascript
import * as THREE from "three/webgpu";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
// renderer is an initialized WebGPURenderer (its init() was awaited)
const gltfLoader = new GLTFLoader()
.setKTX2Loader(new KTX2Loader().detectSupport(renderer));
const gltf = await gltfLoader.loadAsync("model.opt.glb");
scene.add(gltf.scene);上述加载器和 addon 在 WebGPURenderer 下完全相同,WebGPURenderer 会自行将加载的材质渲染为 node 材质( 仍是 GLTFLoader 创建的类)。有一条顺序规则: 需要 renderer's backend,因此必须在 之后创建 loader。
mesh.materialKTX2Loader.detectSupport(renderer)await renderer.init()javascript
import * as THREE from "three/webgpu";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
// renderer is an initialized WebGPURenderer (its init() was awaited)
const gltfLoader = new GLTFLoader()
.setKTX2Loader(new KTX2Loader().detectSupport(renderer));
const gltf = await gltfLoader.loadAsync("model.opt.glb");
scene.add(gltf.scene);Common Mistakes
常见错误
- →
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js": the three/examples/jsm specifier does not resolve under the import map three.js documents.import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js" - →
npx @gltf-transform/cli optimize model.glb out.glb --compress ktx2: texture codecs belong tonpx @gltf-transform/cli optimize model.glb out.glb --compress draco --texture-compress ktx2;--texture-compresstakes draco, meshopt or quantize.--compress - →
npx @gltf-transform/cli draco model.glb a.glb && npx @gltf-transform/cli webp a.glb out.glb --quality 80: every command exceptnpx @gltf-transform/cli webp model.glb a.glb --quality 80 && npx @gltf-transform/cli draco a.glb out.glb,dracoandmeshoptdecodes Draco and meshopt geometry when it reads a file and writes it uncompressed, so geometry compression runs last.optimize - →
new GLTFLoader().loadAsync("draco.glb"): loading a KHR_draco_mesh_compression file without a DRACOLoader throws "No DRACOLoader instance provided".new GLTFLoader().setDRACOLoader(new DRACOLoader()).loadAsync("draco.glb") - →
new GLTFLoader().loadAsync("meshopt.glb"): an EXT_meshopt_compression file throws "setMeshoptDecoder must be called before loading compressed files".new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync("meshopt.glb") - →
new GLTFLoader().loadAsync("ktx2.glb"): a KHR_texture_basisu file throws "setKTX2Loader must be called before loading KTX2 textures".new GLTFLoader().setKTX2Loader(new KTX2Loader().detectSupport(renderer)).loadAsync("ktx2.glb") - →
setKTX2Loader(new KTX2Loader()): a KTX2Loader that never ran detectSupport throws "Missing initialization withsetKTX2Loader(new KTX2Loader().detectSupport(renderer))"..detectSupport( renderer ) - →
dracoLoader.setDecoderPath(cdnPathFromAnotherRelease): the Draco decoder loads from the three package by default, and a path pinned to a different release mismatches the loader.new GLTFLoader().setDRACOLoader(new DRACOLoader()) - →
dracoLoader.setDecoderConfig({ type: "js" }): setDecoderConfig is deprecated in r185; without it DRACOLoader uses the WASM decoder wherever WebAssembly exists.new DRACOLoader() - →
ktx2Loader.detectSupportAsync(renderer): detectSupportAsync is deprecated in r181; call detectSupport once the renderer is initialized.await renderer.init(); ktx2Loader.detectSupport(renderer); - →
npx @gltf-transform/cli dequantize model.glb out.glb: GLTFLoader reads KHR_mesh_quantization attributes with no runtime setup, so dequantize only when your own code needs float vertex data in model units: quantized attributes hold normalized integers, and quantized positions are scaled back by their node's transform.npx @gltf-transform/cli quantize model.glb out.glb
- →
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js":在 three.js 文档提供的 import map 下,import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"说明符无法解析。three/examples/jsm - →
npx @gltf-transform/cli optimize model.glb out.glb --compress ktx2:纹理 codec 应通过npx @gltf-transform/cli optimize model.glb out.glb --compress draco --texture-compress ktx2指定;--texture-compress只接受 draco、meshopt 或 quantize。--compress - →
npx @gltf-transform/cli draco model.glb a.glb && npx @gltf-transform/cli webp a.glb out.glb --quality 80:除npx @gltf-transform/cli webp model.glb a.glb --quality 80 && npx @gltf-transform/cli draco a.glb out.glb、draco和meshopt外,所有命令在读取文件时都会解码 Draco 和 meshopt 几何体,并将其以未压缩形式写出,因此几何体压缩必须最后执行。optimize - →
new GLTFLoader().loadAsync("draco.glb"):加载 KHR_draco_mesh_compression 文件时若未配置 DRACOLoader,会抛出new GLTFLoader().setDRACOLoader(new DRACOLoader()).loadAsync("draco.glb")。No DRACOLoader instance provided - →
new GLTFLoader().loadAsync("meshopt.glb"):EXT_meshopt_compression 文件会抛出new GLTFLoader().setMeshoptDecoder(MeshoptDecoder).loadAsync("meshopt.glb")。setMeshoptDecoder must be called before loading compressed files - →
new GLTFLoader().loadAsync("ktx2.glb"):KHR_texture_basisu 文件会抛出new GLTFLoader().setKTX2Loader(new KTX2Loader().detectSupport(renderer)).loadAsync("ktx2.glb")。setKTX2Loader must be called before loading KTX2 textures - →
setKTX2Loader(new KTX2Loader()):未运行 detectSupport 的 KTX2Loader 会抛出setKTX2Loader(new KTX2Loader().detectSupport(renderer))。Missing initialization with .detectSupport( renderer ) - →
dracoLoader.setDecoderPath(cdnPathFromAnotherRelease):Draco decoder 默认从 three 包加载,指向其他版本路径会导致 decoder 与 loader 不匹配。new GLTFLoader().setDRACOLoader(new DRACOLoader()) - →
dracoLoader.setDecoderConfig({ type: "js" }):setDecoderConfig 在 r185 中已弃用;不调用它时,只要环境支持 WebAssembly,DRACOLoader 就会使用 WASM decoder。new DRACOLoader() - →
ktx2Loader.detectSupportAsync(renderer):detectSupportAsync 在 r181 中已弃用;应在 renderer 初始化完成后调用一次 detectSupport。await renderer.init(); ktx2Loader.detectSupport(renderer); - →
npx @gltf-transform/cli dequantize model.glb out.glb:GLTFLoader 无需运行时配置即可读取 KHR_mesh_quantization 属性,因此仅当自有代码需要以模型单位表示的浮点顶点数据时才应使用 dequantize:量化属性保存的是归一化整数,量化位置会通过所属节点的 transform 还原缩放。npx @gltf-transform/cli quantize model.glb out.glb
See Also
另请参阅
- - the runtime side: loading progress, caching, cloning skinned models and disposal of what the pipeline produces.
threejs-loaders - - the React app around a gltfjsx component: Canvas, useGLTF, Suspense and drei helpers.
threejs-react-three-fiber - - measuring what the pipeline bought: draw calls, GPU memory and renderer.info.
threejs-performance
- ——运行时部分:加载进度、缓存、克隆蒙皮模型,以及释放管线生成的资源。
threejs-loaders - ——围绕 gltfjsx 组件构建 React 应用:Canvas、useGLTF、Suspense 和 drei helper。
threejs-react-three-fiber - ——衡量管线带来的收益:draw call、GPU 内存和 renderer.info。
threejs-performance