Node.js服务器用draco3d压缩GLB文件遇编码错误求助
问题分析与解决
核心问题
你直接将GLB文件的二进制数据(Uint8Array)传给了EncodeMeshToDracoBuffer,但Draco编码器不支持直接处理GLB容器文件。EncodeMeshToDracoBuffer需要的是Draco库定义的Mesh对象(包含顶点、索引、法线等几何属性的结构化数据),而不是原始的GLB二进制。返回0是因为编码失败,后续用Buffer.from(0)自然会触发类型错误。
正确流程
要压缩GLB中的模型,必须先解析GLB提取出网格数据,转换成Draco能识别的格式,再进行编码:
- 解析GLB文件,提取场景中的Mesh几何数据
- 将Mesh的顶点、索引、法线等属性导入Draco的
Mesh对象 - 使用Draco编码器对
Mesh对象进行编码 - 提取编码后的二进制数据并保存为.drc文件
修改后的代码示例
这里用three.js来解析GLB(Node.js环境可用),结合draco3d完成压缩:
首先安装依赖:
npm install three draco3d
然后修改控制器中的Draco处理部分:
const { GLTFLoader } = require('three/examples/jsm/loaders/GLTFLoader'); const { BufferGeometryUtils } = require('three/examples/jsm/utils/BufferGeometryUtils'); const THREE = require('three'); const draco3d = require('draco3d'); // ... 原有代码 ... /*********** DRACO ************** */ const dracoEncoderModule = draco3d.createEncoderModule({}); dracoEncoderModule .then((module) => { const encoder = new module.Encoder(); // 设置压缩参数(可选,值越高压缩效果越好但速度越慢) encoder.SetSpeedOptions(5, 5); // 用three.js解析GLB文件 const loader = new GLTFLoader(); loader.load( pathGLB, (gltf) => { // 提取模型中的所有网格 const geometries = []; gltf.scene.traverse((child) => { if (child.isMesh) { geometries.push(child.geometry); } }); if (geometries.length === 0) { console.error('GLB文件中未找到Mesh数据'); return; } // 合并多个网格(如果模型有多个部件) const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries); // 将three.js的BufferGeometry转换为Draco的Mesh const dracoMesh = new module.Mesh(); // 导入顶点数据 const positionArray = mergedGeometry.attributes.position.array; const positionAttribute = new module.Attribute(); positionAttribute.Init(3, module.TYPE_FLOAT32, positionArray.length / 3, new module.DracoFloat32Array(positionArray)); dracoMesh.AddAttribute(module.POSITION, positionAttribute); // 导入索引数据(如果存在) if (mergedGeometry.index) { const indexArray = mergedGeometry.index.array; const indexAttribute = new module.Attribute(); indexAttribute.Init(1, module.TYPE_UINT32, indexArray.length, new module.DracoUInt32Array(indexArray)); dracoMesh.AddAttribute(module.INDICES, indexAttribute); } // 导入法线数据(可选,提升渲染效果) if (mergedGeometry.attributes.normal) { const normalArray = mergedGeometry.attributes.normal.array; const normalAttribute = new module.Attribute(); normalAttribute.Init(3, module.TYPE_FLOAT32, normalArray.length / 3, new module.DracoFloat32Array(normalArray)); dracoMesh.AddAttribute(module.NORMAL, normalAttribute); } // 执行编码 const encodedBuffer = new module.DracoInt8Array(); const encodingResult = encoder.EncodeMeshToDracoBuffer(dracoMesh, encodedBuffer); if (encodingResult === module.SUCCESS) { // 转换为Node.js Buffer并保存 const encodedData = Buffer.from(encodedBuffer.buffer, encodedBuffer.byteOffset, encodedBuffer.length); fs.writeFileSync(pathDRC, encodedData); console.log('Draco压缩完成'); } else { console.error('Draco编码失败,错误码:', encodingResult); } // 释放WebAssembly内存 module.destroy(dracoMesh); module.destroy(encoder); module.destroy(encodedBuffer); }, undefined, (err) => { console.error('解析GLB失败:', err); } ); }) .catch((error) => { console.error('初始化Draco编码器失败:', error); }); /************************* */
关键说明
- GLB解析:three.js的
GLTFLoader能正确解析GLB中的场景、Mesh等数据,遍历场景可提取所有网格并合并(适配多部件模型)。 - Draco Mesh构建:必须手动将three.js的几何属性(顶点、索引、法线等)导入Draco的
Attribute对象,再添加到Draco的Mesh中,这是Draco编码器唯一能识别的输入格式。 - 内存管理:Draco的WebAssembly模块创建的对象需要手动调用
module.destroy()释放,避免内存泄漏。 - 压缩参数:
SetSpeedOptions可调整压缩速度和质量,第一个参数是编码速度(1-10),第二个是解码速度(1-10),可根据业务需求平衡选择。
内容的提问来源于stack exchange,提问作者Ruslan Karimov
相关产品推荐
相关产品推荐

