之前采用unity3d三维引擎进行web项目的开发,但因为存在较长的前期加载黑屏加载时间,故采用新的三维技术方案进行项目开发:three.js+glb。因为甲方项目中的模型多数为工业模型,所以模型大,面数多,three.js场景本身的模型加载就有一定的上限,所以模型的轻量化是一个较为重要的问题。
DRACOLoader,three.js本身的glb格式模型轻量化加载工具便成为了第一个研究使用对象。
能从three.js找到的demo场景里对DRACOLoader使用的代码:
new GLTFLoader()
.setPath( 'models/gltf/' )
.setDRACOLoader( new DRACOLoader().setDecoderPath( 'js/libs/draco/gltf/' ) )
.load( 'model-separate.glb', function ( gltf ) {
console.log(gltf.scene);
scene.add( gltf.scene );
} );
就以现有的场景作为研究对象,发现DRACOLoader只是一个对进行压缩后的模型进行特殊加载的工具,本身并不能实现对glb模型的压缩功能。
实际用来做glb压缩的工具为gltf-pipeline,故先进行gltf-pipeline的安装操作。GitHub地址https://github.com/CesiumGS/gltf-pipeline。
先安装nodejs,地址下载 | Node.js。Nodejs安装好后,打开Developer PowerShell for VS 2019,输入npm install -g gltf-pipeline安装gltf-pipeline。
安装vscode,在桌面创建一个glb文件夹,然后使用vscode打开glb文件夹,创建文件draco.js作为压缩代码脚本文件,打开终端,新建终端,输入npm install gltf-pipeline,当安装成功后左边窗口会出现node_modules文件夹,以及两个json文件。将需要压缩的glb模型(model.glb)放到刚才创建在桌面的glb文件夹中。
先将glb转成gltf:
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const glbToGltf = gltfPipeline.glbToGltf;
const glb = fsExtra.readFileSync("model.glb");
glbToGltf(glb).then(function (results) {
fsExtra.writeJsonSync("model.gltf", results.gltf);
});
再压缩gltf:
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const processGltf = gltfPipeline.processGltf;
const gltf = fsExtra.readJsonSync("model.gltf");
const options = {
dracoOptions: {
compressionLevel: 10,
},
};
processGltf(gltf, options).then(function (results) {
fsExtra.writeJsonSync("model-draco.gltf", results.gltf);
});
再将压缩过后的gltf转换成glb:
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const gltfToGlb = gltfPipeline.gltfToGlb;
const gltf = fsExtra.readJsonSync("model-draco.gltf");
gltfToGlb(gltf).then(function (results) {
fsExtra.writeFileSync("model-draco.glb", results.glb);
});
然而采取以上代码压缩会发现,实际压缩效果并没有太好,不存在10倍压缩的情况,将gltf纹理单独保存出来就会发现,纹理贴图是没有做过压缩处理的,只是将本身的gltf做了压缩,本身gltf确实存在10倍压缩,所以如果你的模型贴图占了该模型大小较重的比例,这个压缩实际效果很差。
所以就需要对gltf的贴图进行压缩。
先将gltf做纹理的单独保存:
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const processGltf = gltfPipeline.processGltf;
const gltf = fsExtra.readJsonSync("model.gltf");
const options = {
separateTextures: true,
};
processGltf(gltf, options).then(function (results) {
fsExtra.writeJsonSync("model-separate.gltf", results.gltf);
// Save separate resources
const separateResources = results.separateResources;
for (const relativePath in separateResources) {
if (separateResources.hasOwnProperty(relativePath)) {
const resource = separateResources[relativePath];
fsExtra.writeFileSync(relativePath, resource);
}
}
});
Nodejs有个jimp模块可以对图片进行处理,在glb工程中打开终端,输入npm install jimp,安装jimp工具。
图片压缩代码:
var Jimp = require('jimp');
Jimp.read('image0.png').then(img => {
const imgWidth = img.bitmap.width;
const imgHeight = img.bitmap.height;
const length = 400;
const isWidthLonger = imgWidth > imgHeight ? true : false;
const time = (isWidthLonger ? imgWidth : imgHeight) / length;
const rWidth = imgWidth / time;
const rHeight = imgHeight / time;
return img.resize(rWidth, rHeight ).write(`image0.png`);
});
然后把单独保存出来并处理过的图片和gltf,重新再合成glb。
使用CMD命令行进行gltf以及纹理贴图的打包,生产glb:
[pgltf-pipeline -i ‘gltf模型文件地址’ -o ‘glb模型文件保存地址’],保证纹理和gltf模型文件在一个文件夹下,如此便成功将glb进行网格和纹理的压缩。
最后代码总结:
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const glbToGltf = gltfPipeline.glbToGltf;
const glb = fsExtra.readFileSync("model.glb");
const processGltf = gltfPipeline.processGltf;
var Jimp = require('jimp');
const options = {
dracoOptions: {
compressionLevel: 10,
},
};
const options1 = {
separateTextures: true,
};
glbToGltf(glb).then(function (results) {
processGltf(results.gltf, options).then(function (results) {
processGltf(results.gltf, options1).then(function (results) {
fsExtra.writeJsonSync("model-separate.gltf", results.gltf);
// Save separate resources
const separateResources = results.separateResources;
for (const relativePath in separateResources) {
if (separateResources.hasOwnProperty(relativePath)) {
const resource = separateResources[relativePath];
fsExtra.writeFileSync(relativePath, resource);
Jimp.read(relativePath).then(img => {
const imgWidth = img.bitmap.width;
const imgHeight = img.bitmap.height;
const length = 10;
const rWidth = imgWidth / length;
const rHeight = imgHeight / length;
return img.resize(rWidth, rHeight ).write(relativePath);
});
}
}
});
});
});
CMD:gltf-pipeline -i C:\xx\xx\xx\glb\model-separate.gltf -o C:\xx\xx\xx\glb\model-separate.glb
最后4158KB的glb压缩到138KB,并且正常加载使用