three.js DRACOExporter:将 Mesh 与点云导出为 Draco 压缩 .drc 文件的完整指南

发布时间:2026/9/7 10:00:30
three.js DRACOExporter:将 Mesh 与点云导出为 Draco 压缩 .drc 文件的完整指南 three.js DRACOExporter将 Mesh 与点云导出为 Draco 压缩 .drc 文件的完整指南【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本文基于 three.js 仓库中的 DRACOExporter 官方文档 与 源码实现 展开讲解如何使用该 Addon 把Mesh或Points对象导出为 Draco 压缩的.drc二进制数据。读完本文你将掌握parseAsync()的完整调用方式、全部 7 个导出选项decodeSpeed、encodeSpeed、encoderMethod、quantization、exportUvs、exportNormals、exportColor的默认值与底层作用以及从源码层面理解编码器如何组织顶点、面索引与量化的整个流程。一、Draco 是什么DRACOExporter 能做什么DRACOExporter 是 three.js 提供的一个导出器exporter用于借助 Google 开源的 Draco 库压缩 3D 网格与点云几何体。压缩后的几何数据可以显著减小体积代价是客户端需要付出额外的解码时间。关于独立 Draco 文件文档中明确了它的“能”与“不能”独立.drc文件包含顶点位置POSITION、法线NORMAL、颜色COLOR以及其他顶点属性独立.drc文件不包含材质materials、贴图textures、动画animation、节点层级node hierarchies。这一点决定了.drc的定位它只保存“几何数据”是纯粹的几何压缩容器。如果需要导出带材质、动画的完整场景并使用 Draco 压缩正确做法是把 Draco 几何体嵌入 glTF 文件中three.js 的GLTFExporter也支持此流程而不是依赖DRACOExporter本身。从源码头部注释看DRACOExporter.js 第 5~27 行 的类文档与官方文档完全一致并给出了最小用法示例const exporter new DRACOExporter(); const data await exporter.parseAsync( mesh, options );二、前置条件必须先加载 Draco 编码器DRACOExporter依赖一个全局脚本DracoEncoderModuleDraco 的 WebAssembly/JS 编码器构建产物。这是使用该导出器的硬性前置条件也是很多初学者踩的坑。官方示例 misc_exporter_draco.html 的做法是在页面中先引入编码器以 1.5.7 版本为例script srchttps://cdn.jsdelivr.net/gh/google/draco1.5.7/javascript/draco_encoder.js/script编辑器 editor/index.html 同样以相同方式引入了该全局脚本说明这是仓库内所有使用DRACOExporter的场景的统一做法。源码中对此有显式校验——第 53~57 行if ( typeof DracoEncoderModule undefined ) { throw new Error( THREE.DRACOExporter: required the draco_encoder to work. ); }也就是说如果忘记加载编码器parseAsync()会直接抛出THREE.DRACOExporter: required the draco_encoder to work.错误而不是静默失败。另外源码 第 63~65 行 还处理了新旧编码器构建的兼容问题let dracoEncoder DracoEncoderModule(); // older encoder builds expose the module synchronously, newer builds return a promise if ( dracoEncoder.Encoder undefined ) dracoEncoder await dracoEncoder;旧版编码器同步暴露模块新版返回 PromiseparseAsync对两者都做了适配因此无论加载哪个版本构建都能工作。三、导入方式与构造函数DRACOExporter属于 three.js 的 Addon需要显式导入而非从three主包引入import { DRACOExporter } from three/addons/exporters/DRACOExporter.js;构造函数无任何参数const exporter new DRACOExporter();实例化后即可反复调用parseAsync()导出多个对象编码器实例是在每次parseAsync()内部创建并销毁的见后文源码分析导出器本身是无状态的。四、核心 APIparseAsync( object, options )4.1 方法签名.parseAsync( object : Mesh | Points, options : DRACOExporter~Options ) : Promise.Int8Array (async)参数说明object要导出的对象只支持Mesh或Points两种类型其他类型会抛出THREE.DRACOExporter: Unsupported object type.错误options导出选项可省略缺省时使用全部默认值返回值一个 Promiseresolve 的结果是Int8Array即完整的.drc二进制内容注意旧 API 的状态文档标注.parse()已被弃用源码中它现在只是一个抛错的占位实现第 240~244 行parse() { throw new Error( THREE.DRACOExporter: parse() has been replaced by parseAsync(). ); }如果你在旧教程中看到exporter.parse( mesh, callback )的写法迁移到新版本的唯一方式就是换成await exporter.parseAsync( mesh, options )。4.2 Options 参数完整参考options对象支持以下 7 个字段缺省值来自 源码第 43~51 行 的Object.assign参数类型默认值说明decodeSpeednumber5提示编码器如何针对解码速度调优。取值 0~100 表示解码最快但压缩质量压缩率最差10 反之encodeSpeednumber5提示编码器如何调优编码参数。0 表示编码最快但压缩质量最差10 反之encoderMethodnumber1即MESH_EDGEBREAKER_ENCODING编码方式0为顺序编码几乎不压缩1为 Edgebreaker 编码。Edgebreaker 以确定的螺旋状方式遍历网格三角形能提供该数据格式的大部分压缩收益quantizationArraynumber[ 16, 8, 8, 8, 8 ]按(POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)的顺序指定 draco 文件中每类数据使用的量化位数即精度exportUvsbooleantrue是否导出 UV贴图坐标exportNormalsbooleantrue是否导出法线exportColorbooleanfalse是否导出顶点颜色几个值得注意的调优要点速度参数方向源码 第 169 行 注释写明“从 0最慢速度但最佳压缩到 10最快但压缩最差”随后调用encoder.SetSpeedOptions( encodeSpeed, decodeSpeed )。所以追求文件体积最小应把两个速度值调小如 0追求编码/解码实时性则调大。量化位数与体积/精度权衡quantization数组对应 5 类属性。默认位置属性 16 位量化其余 8 位——位置保留较高精度以保证形状保真法线/颜色/UV 则用 8 位大幅压缩。源码 第 186~198 行 会对数组前 5 个有效元素逐个调用encoder.SetAttributeQuantization( i, bits )数组中缺失的项undefined会被跳过因此你只传[ 12, 8 ]这样的部分数组也是合法的。Edgebreaker vs 顺序编码encoderMethod通过encoder.SetEncodingMethod()设置第 178~182 行。默认值就是 Edgebreaker对三角面网格压缩收益最大顺序编码基本不做压缩一般只在特殊场景使用。4.3 最小可运行示例结合官方示例 misc_exporter_draco.html 的核心逻辑一次完整的导出 保存流程如下!-- 前置条件先加载编码器全局脚本同官方示例 misc_exporter_draco.html --import * as THREE from three; import { DRACOExporter } from three/addons/exporters/DRACOExporter.js; // 创建场景与被导出的网格 const geometry new THREE.TorusKnotGeometry( 0.75, 0.2, 200, 30 ); const material new THREE.MeshPhongMaterial( { color: 0x00ff00 } ); const mesh new THREE.Mesh( geometry, material ); // 执行导出 const exporter new DRACOExporter(); async function exportFile() { const result await exporter.parseAsync( mesh ); // 返回 Int8Array // 保存为文件 const link document.createElement( a ); link.href URL.createObjectURL( new Blob( [ result ], { type: application/octet-stream } ) ); link.download file.drc; link.click(); }导出结果是Int8Array官方示例把它包装成Blob触发浏览器下载得到可直接被 Draco 工具链、DRACOLoader或 PLY/OBJ 转换工具消费的.drc文件。五、源码级流程解析parseAsync 内部发生了什么阅读 parseAsync 完整实现 可以还原出完整的处理链路这有助于理解各选项到底影响了什么。5.1 按对象类型分两条流水线parseAsync(object) ├─ object.isMesh true │ → MeshBuilder Mesh │ → AddFloatAttributeToMesh(POSITION) // 顶点位置必导 │ → AddFacesToMesh( 面索引 ) // 有索引用索引无索引则自动构造 │ → 可选: NORMAL / TEX_COORD / COLOR 属性 └─ object.isPoints true → PointCloudBuilder PointCloud → AddFloatAttribute(POSITION) // 顶点位置必导 → 可选: COLOR 属性关键点位置属性始终导出且是唯一不受选项开关控制的属性法线、UV、颜色分别在exportNormals、exportUvs、exportColor为true且几何体确实存在对应 attribute 时才会加入第 99~135 行。无索引几何体的自动兜底如果Mesh的geometry.getIndex()为null源码 第 85~97 行 会现场生成一个顺序索引数组顶点数超过 65535 时自动选用Uint32Array否则Uint16Array再交给AddFacesToMesh。这意味着即使你直接拿new THREE.PlaneGeometry()之类默认就带索引的几何体、或手动去除了索引导出也不会失败。非 Mesh / Points 直接抛错第 159~163 行例如Group、Sprite都不支持。5.2 编码、量化的落点// [第 171~198 行] const encodeSpeed ( options.encodeSpeed ! undefined ) ? options.encodeSpeed : 5; const decodeSpeed ( options.decodeSpeed ! undefined ) ? options.decodeSpeed : 5; encoder.SetSpeedOptions( encodeSpeed, decodeSpeed ); if ( options.encoderMethod ! undefined ) { encoder.SetEncodingMethod( options.encoderMethod ); } for ( let i 0; i 5; i ) { if ( options.quantization[ i ] ! undefined ) { encoder.SetAttributeQuantization( i, options.quantization[ i ] ); } }三类参数最终都映射到 Draco C 编码器经 emscripten 封装出的 API速度 →SetSpeedOptions编码方式 →SetEncodingMethod量化 →SetAttributeQuantization按属性类型 0~4 逐个设置数组按(POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)顺序对应这些类型下标。5.3 顶点颜色有一个容易被忽略的 sRGB 转换exportColor: true时顶点颜色不是直接复制原始 Float32 数据而是先经过 createVertexColorSRGBArrayfunction createVertexColorSRGBArray( attribute ) { // While .drc files do not specify colorspace, the only official tooling // is PLY and OBJ converters, which use sRGB. Well assume sRGB is expected // for .drc files, but note that Draco buffers embedded in glTF files will // be Linear-sRGB instead. _color.fromBufferAttribute( attribute, i ); ColorManagement.workingToColorSpace( _color, SRGBColorSpace ); ... }原因在注释里写得很清楚.drc格式本身不声明色彩空间而官方生态PLY/OBJ 转换器默认按 sRGB 处理因此 three.js 会主动把 working color space 的颜色转换到 sRGB 再写入反之Draco 缓冲区嵌入 glTF 时则使用 Linear-sRGB。如果你在往返测试中发现顶点颜色“偏亮/偏暗”这里就是差异来源。5.4 输出拷贝与资源释放编码完成后第 200~233 行const encodedData new dracoEncoder.DracoInt8Array(); if ( object.isMesh true ) { length encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData ); } else { length encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData ); }返回长度为 0 时视为编码失败抛出THREE.DRACOExporter: Draco encoding failed.随后把 WASM 侧的DracoInt8Array逐字节拷贝到一个全新的Int8Arraynew ArrayBuffer( length )中再返回——这样调用方拿到的就是纯 JS 侧内存与 emscripten 堆解耦最后显式destroy了dracoObject、encodedData、encoder、builder四个 WASM 对象防止长时间导出多个模型时的内存泄漏。5.5 类上的一组常量除了文档列出的两个编码方式常量源码还在类上定义了完整的属性类型枚举第 283~317 行常量值用途MESH_EDGEBREAKER_ENCODING1Edgebreaker 编码默认对应options.encoderMethodMESH_SEQUENTIAL_ENCODING0顺序编码几乎不压缩POINT_CLOUD0几何类型枚举TRIANGULAR_MESH1几何类型枚举POSITION/NORMAL/COLOR/TEX_COORD/GENERIC0~4属性类型即quantization数组下标顺序INVALID-1无效属性其中MESH_EDGEBREAKER_ENCODING与MESH_SEQUENTIAL_ENCODING是文档明确列出的“Properties”后几组是内部流程使用的类型编号理解quantization数组时正好可以对照。六、官方示例misc_exporter_draco仓库自带一个可交互演示 misc_exporter_draco.html截图即文首配图完整演示了本文的全部要点页面顶部加载draco_encoder.js全局脚本第 18 行通过 import map 把three/addons/映射到仓库的./jsm/目录第 20~27 行因此该示例可以直接在源码仓库中静态托管运行用TorusKnotGeometry( 0.75, 0.2, 200, 30 )建了一个带光照与阴影的网格场景第 85~90 行通过 lil-gui 提供 “Export DRC” 按钮点击后调用await exporter.parseAsync( mesh )并把结果保存为file.drc第 134~139 行。注意该示例没有传options即完全使用默认参数导出若需调整压缩率/精度按第四节表格传参即可。七、仓库内其他使用场景three.js 编辑器DRACOExporter在 three.js 自带的场景编辑器里也是一等公民。editor/js/Menubar.File.js 第 239~267 行 实现了 “File → Export → DRC” 菜单项其用法值得参考——它展示了按几何体实际内容自适应选项的写法const options { decodeSpeed: 5, encodeSpeed: 5, encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING, quantization: [ 16, 8, 8, 8, 8 ], exportUvs: true, exportNormals: true, exportColor: object.geometry.hasAttribute( color ) }; const result await exporter.parseAsync( object, options ); saveArrayBuffer( result, model.drc );可以看到编辑器只在选中对象是 Mesh 时才允许导出否则弹出 noMeshSelected 提示并且把exportColor动态设置为“几何体是否真的带 color 属性”——比无脑true更严谨因为源码中颜色导出在colors undefined时本来就会静默跳过但显式控制可以让选项语义与几何体状态一致。编辑器页面 editor/index.html 也已预加载了编码器脚本。八、使用限制与注意事项对象类型受限仅支持Mesh与Points传Group/Sprite等会抛错导出的也是单个对象不支持整场景遍历。.drc不含场景语义无材质、贴图、动画、层级。需要完整场景 Draco 压缩时应走 glTF 路线Draco 嵌入 glTF而不是用本导出器。编码器版本仓库示例锁定 draco 1.5.7 的draco_encoder.js且该脚本必须作为全局脚本先于parseAsync()加载源码同时兼容同步/异步两种模块暴露方式。往返精度位置默认 16 位量化、其余 8 位导出后再加载回 three.js 的几何体会存在轻微数值偏差若你的应用对位置精度敏感如 CAD 类可上调quantization[ 0 ]到 17~20Draco 上限 24 位。旧代码迁移parse()已弃用且直接抛错一律改用parseAsync()并配合await。色彩空间顶点颜色导出时会转换到 sRGB见 5.3 节往返对比颜色时需注意。九、小结DRACOExporter的 API 面很小——一个无参构造函数、一个parseAsync()、七个选项但它把“three.js 内存中的几何体 → Draco 压缩二进制”这条链路封装得非常干净自动处理索引兜底、WASM 编码器新旧版本兼容、量化参数逐属性下发、sRGB 颜色转换与 WASM 资源释放。默认配置Edgebreaker 速度 5 [16,8,8,8,8]量化适合大多数网格压缩场景需要更极致的压缩率时调低encodeSpeed/decodeSpeed并提高量化位数即可。官方示例 misc_exporter_draco.html 与 editor/js/Menubar.File.js 中的编辑器实现是两个可以直接对照抄写的实战范本。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考