☰
Blender导出glb与Three.js加载:Web 3D模型全流程实战指南
2026/9/30 9:45:05 网站建设 项目流程

1. 从 Blender 到浏览器:glTF/glb 到底解决了什么问题

如果你做过三维可视化项目,大概率经历过这样的场景:美术在 Blender 里精雕细琢了一个模型,导出成 OBJ 加一堆贴图文件,然后前端同事拿到手发现材质全丢了,法线反了,缩放比例也不对。更头疼的是,OBJ 格式不支持动画、不支持 PBR 材质、不支持场景层级,你只能靠一堆零散文件拼凑出一个勉强能看的模型。glTF 的出现,本质上就是为了终结这种混乱。

glTF 的全称是 Graphics Language Transmission Format,由 Khronos Group 制定并维护。它被业界称为“3D 界的 JPEG”,这个比喻非常精准——就像 JPEG 用最小的体积传递一张图片的完整视觉信息,glTF 用最小的开销传递一个 3D 场景的完整描述:网格、材质、纹理、动画、骨骼、相机、灯光,全部打包在一个结构化的文件里。而 .glb 是 glTF 的二进制版本,把 JSON 描述和所有外部资源(贴图、二进制缓冲区)合并成一个文件,方便传输和加载。

为什么现在几乎所有主流 3D 引擎和框架都在推 glTF?核心原因有三个。第一,它是开放标准,不属于任何一家公司,不会出现某天突然收费或者停止维护的情况。第二,它的设计目标就是“运行时友好”——解析速度快,数据结构贴近 GPU 的渲染管线,不需要在加载时做大量转换。第三,生态成熟,Blender、Maya、3ds Max、C4D 等主流建模工具都原生支持导出 glTF/glb,Three.js、Babylon.js、model-viewer、Unity、Unreal 等引擎也都原生支持加载。

对于做 Web 3D 展示的开发者来说,glTF/glb 几乎是唯一正确的选择。你不需要再纠结 OBJ、FBX、STL 之间的格式转换,也不需要自己写解析器去处理各种私有格式。Blender 导出 glb,前端直接加载,材质、动画、层级关系全部保留,这就是标准化的力量。

注意:glTF 有两个版本,1.0 和 2.0。现在说的 glTF 默认都是 2.0,1.0 已经基本淘汰。导出时务必确认选择的是 glTF 2.0 格式。

2. Blender 导出 glb 时那些没人告诉你的细节

很多人以为导出 glb 就是点一下“文件 → 导出 → glTF 2.0”就完事了,结果到了前端发现模型要么黑乎乎一片,要么比例大得离谱,要么动画根本不播放。这些问题绝大多数不是加载端的锅,而是导出设置没搞对。我踩过至少五六次坑之后,总结出一套相对稳妥的导出流程。

2.1 导出前的场景清理与单位确认

Blender 默认的单位是米,但很多建模教程里大家习惯用厘米甚至毫米来建模。如果你在 Blender 里建了一个 100 单位高的模型,导出 glb 后到了 Three.js 场景里,它可能就是 100 米高的庞然大物,相机根本看不到全貌。所以在导出之前,先确认场景单位:在“场景属性”面板里检查“单位”设置,确保“单位系统”是公制,“长度”是米,“缩放”是 1.0。

另一个容易被忽略的是场景中的隐藏对象。Blender 导出 glb 时默认会导出所有可见对象,但如果你有一些辅助线、空物体、参考图没有删除,它们也会被打包进去。虽然不影响渲染,但会增加文件体积,还可能干扰前端的场景遍历逻辑。导出前按A全选,然后Shift + D复制一份到新场景做清理,或者直接用“文件 → 外部数据 → 清理未使用数据”来移除孤立数据块。

2.2 材质与贴图的打包策略

glTF 2.0 支持 PBR 材质,具体来说是 Metallic-Roughness 工作流。Blender 的 Principled BSDF 节点和 glTF 的 PBR 模型几乎是一一对应的,但有几个细节需要注意。

基础色贴图、金属度贴图、粗糙度贴图、法线贴图、自发光贴图,这些在 Blender 里如果用的是独立节点,导出时 glTF 导出器会自动识别并打包。但如果你用了复杂的节点组,比如混合了多个纹理、加了颜色渐变、用了程序化纹理,导出器可能无法正确转换,结果就是材质丢失或者变成纯色。

我的建议是:导出前把材质简化到 Principled BSDF 的标准输入。程序化纹理如果必须保留,就在 Blender 里烘焙成图片贴图,然后再连接到 Principled BSDF。烘焙这个步骤虽然麻烦,但能保证导出后的材质和你在 Blender 里看到的一致。

贴图格式方面,glb 内嵌的贴图建议用 PNG 或 JPEG。PNG 支持透明通道但体积大,JPEG 体积小但不支持透明。如果模型有透明部分(比如玻璃、树叶),用 PNG;如果没有,用 JPEG 并适当压缩质量,能显著减小文件体积。

2.3 动画与骨骼的导出陷阱

如果你的模型带骨骼动画,导出时一定要勾选“动画”选项,并且确认“动画模式”选择的是“动作”还是“NLA 轨道”。这两个的区别在于:动作模式只导出当前激活的动作,NLA 轨道模式会导出所有 NLA 轨道上的动画片段。如果你有多个动画(比如 idle、walk、run),建议用 NLA 轨道模式,这样前端可以通过动画名称来切换。

还有一个坑是骨骼的缩放。Blender 里骨骼可以有非均匀缩放,但 glTF 对骨骼缩放的支持有限,导出后可能导致动画变形。导出前在姿态模式下检查所有骨骼的缩放值,确保都是 1.0。如果有非 1.0 的缩放,用Ctrl + A应用缩放。

另外,glTF 导出器默认会导出“形变骨骼”和“辅助骨骼”,如果你有一些 IK 控制器、空骨骼不想被导出,可以在导出设置里勾选“仅导出形变骨骼”,或者在 Blender 里把这些骨骼标记为“不导出”。

2.4 导出参数的实际含义与推荐值

Blender 的 glTF 导出面板有一堆选项,很多人直接默认一路下一步。我把关键参数列出来,并给出实际项目中的推荐值。

参数含义推荐值说明
格式输出格式glb单文件,方便传输
包含导出内容选中对象/可见对象按需选择
变换坐标变换+Y 向上Web 3D 标准
几何数据网格导出应用修改器确保修改器生效
材质材质导出导出保留 PBR 材质
动画动画导出按需勾选有动画才勾
压缩网格压缩可选 Draco减小体积但增加解码开销

坐标变换这里特别说明一下。Blender 默认是 +Z 向上,而 glTF 标准是 +Y 向上。导出器会自动做转换,但如果你在 Blender 里已经手动旋转过模型,可能会双重转换导致方向错误。最稳妥的做法是:在 Blender 里保持 +Z 向上建模,导出时让导出器自动转换,不要手动旋转。

Draco 压缩能把网格体积减小 50% 到 80%,但代价是加载时需要额外的解码器,而且解码后的网格精度会有轻微损失。对于移动端或者网络条件差的场景,Draco 很值得;对于桌面端或者局域网场景,可以不压缩,换取更快的加载速度。

3. 前端加载 glb 的完整链路与核心 API

模型导出没问题了,接下来就是前端怎么把它加载进来。不管你用的是 Three.js、Babylon.js 还是 model-viewer,底层的加载逻辑是相通的:读取文件 → 解析 JSON 结构 → 创建缓冲区 → 构建网格和材质 → 添加到场景。理解这个链路,能帮你在出问题时快速定位是哪一步卡住了。

3.1 用 Three.js 加载 glb 的最小可用代码

Three.js 是目前 Web 3D 最流行的库,它的 GLTFLoader 也是用得最多的 glb 加载器。下面这段代码是我在实际项目中反复使用的最小可用版本,去掉了所有花哨的东西,只保留核心逻辑。

import * as THREE from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; // 1. 创建场景、相机、渲染器 const scene = new THREE.Scene(); scene.background = new THREE.Color(0x1a1a1a); const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.set(3, 3, 5); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; document.body.appendChild(renderer.domElement); // 2. 添加环境光照和方向光 const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); directionalLight.position.set(5, 10, 7); scene.add(directionalLight); // 3. 加载 glb 模型 const loader = new GLTFLoader(); loader.load( '/models/my-model.glb', (gltf) => { const model = gltf.scene; model.scale.set(1, 1, 1); model.position.set(0, 0, 0); scene.add(model); // 如果有动画,创建混合器并播放 if (gltf.animations && gltf.animations.length > 0) { const mixer = new THREE.AnimationMixer(model); gltf.animations.forEach((clip) => { mixer.clipAction(clip).play(); }); // 在渲染循环中更新 mixer const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); mixer.update(delta); renderer.render(scene, camera); } animate(); } else { function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); } }, (xhr) => { // 加载进度 const percent = (xhr.loaded / xhr.total) * 100; console.log(`加载进度:${percent.toFixed(1)}%`); }, (error) => { console.error('模型加载失败:', error); } ); // 4. 添加轨道控制器 const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; controls.target.set(0, 1, 0);

这段代码里几个关键点值得展开说。renderer.toneMapping设置成 ACESFilmic 能让 PBR 材质的色彩过渡更自然,不加的话模型可能看起来发灰或者过曝。setPixelRatio限制在 2 以内是为了在高分屏上避免性能浪费,超过 2 的像素比肉眼几乎看不出区别,但渲染开销翻倍。

加载回调里的gltf.scene是一个 Group,包含了模型的所有网格、骨骼、动画信息。你可以直接把它加到场景里,也可以遍历它的子对象做进一步处理,比如修改某个网格的材质、调整某个节点的位置。

3.2 加载进度、错误处理与超时控制

实际项目中,模型文件可能几 MB 到几十 MB,用户需要看到加载进度,否则会以为页面卡死了。GLTFLoader 的onProgress回调提供了loaded和total两个参数,但要注意total在某些服务器配置下可能是 0,这时候需要做兼容处理。

loader.load( url, onLoad, (xhr) => { if (xhr.total > 0) { const percent = (xhr.loaded / xhr.total) * 100; updateProgressBar(percent); } else { // 无法获取总大小时,显示已加载字节数 updateProgressBar(null, xhr.loaded); } }, onError );

错误处理方面,常见的失败原因有:文件路径错误、跨域限制、文件损坏、内存不足。建议在onError回调里根据error类型给出不同的用户提示。如果是网络错误,提示“网络异常,请重试”;如果是解析错误,提示“模型文件损坏”;如果是内存错误,提示“设备性能不足”。

超时控制可以用AbortController来实现。给 loader 的load方法传入一个signal,然后在 setTimeout 里调用abort(),这样如果模型在指定时间内没加载完,就会自动取消并触发错误回调。

3.3 模型加载后的常见调整:缩放、居中、朝向

从 Blender 导出的模型,加载到 Three.js 后经常需要做三件事:缩放、居中、调整朝向。

缩放的问题前面提过,如果 Blender 单位没统一,模型可能过大或过小。一个实用的技巧是:加载后计算模型的包围盒,然后根据包围盒尺寸自动缩放到一个目标大小。

const box = new THREE.Box3().setFromObject(model); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const targetSize = 2; // 目标最大边长 const scale = targetSize / maxDim; model.scale.setScalar(scale);

居中则是把模型的几何中心移到原点。计算包围盒的中心,然后取反偏移:

const center = box.getCenter(new THREE.Vector3()); model.position.sub(center.multiplyScalar(scale));

朝向问题通常出现在从其他软件导出的模型上。glTF 标准是 +Y 向上、+Z 向前,但有些软件导出的是 +Z 向上。如果模型躺倒了,可以旋转 -90 度绕 X 轴来修正。不过更好的做法是在导出前就统一好坐标系,避免在加载端做额外旋转。

4. 性能优化:让大模型在浏览器里跑得动

一个 50MB 的 glb 模型,在高端台式机上可能流畅运行,到了中低端手机上就是灾难。性能优化不是可选项,而是必选项。下面这几个方向是我在实际项目中验证过有效的。

4.1 网格减面与 LOD 策略

模型面数是性能的第一杀手。一个 100 万面的模型,即使什么都不做,光是渲染就要消耗大量 GPU 资源。Blender 里有几种减面方式:Decimate 修改器、Remesh 修改器、手动重拓扑。Decimate 最快,但减面后网格质量一般;Remesh 能生成更均匀的网格,但计算慢;手动重拓扑质量最好,但最耗时。

对于 Web 展示场景,我的经验是:静态展示模型控制在 5 万面以内,可交互模型控制在 20 万面以内,需要实时动画的模型控制在 10 万面以内。超过这个数,中低端设备就会明显掉帧。

LOD(Level of Detail)是另一个有效策略。根据相机距离切换不同精度的模型:近处用高模,远处用低模。Three.js 本身没有内置 LOD 加载器,但可以通过监听相机距离手动切换model.visible或者替换model.children来实现。

4.2 纹理压缩与 KTX2 格式

纹理往往是 glb 文件体积的大头。一张 4096x4096 的 PNG 贴图可能就有 10MB 以上。压缩纹理有两个方向:降低分辨率和使用压缩格式。

分辨率方面,Web 展示场景下 2048x2048 通常足够,移动端 1024x1024 也够用。除非是特写展示,否则没必要上 4096。

压缩格式方面,KTX2 是目前 Web 3D 的主流选择。它支持 GPU 直接解码,不需要在 CPU 端解压,能显著减少内存占用和加载时间。Three.js 的 KTX2Loader 可以配合 GLTFLoader 使用,自动处理 KTX2 纹理。

import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js'; const ktx2Loader = new KTX2Loader() .setTranscoderPath('/path/to/basis/') .detectSupport(renderer); const gltfLoader = new GLTFLoader() .setKTX2Loader(ktx2Loader);

使用 KTX2 需要在 Blender 导出时就把贴图转成 KTX2 格式,或者用toktx工具做后处理。这一步稍微麻烦,但对于大模型来说,收益非常明显。

4.3 按需加载与实例化渲染

如果场景里有多个相同模型,不要重复加载。用clone()方法复制模型,或者用 InstancedMesh 做实例化渲染。InstancedMesh 能把 100 个相同网格的绘制调用从 100 次降到 1 次,性能提升是数量级的。

const geometry = originalMesh.geometry; const material = originalMesh.material; const count = 100; const instancedMesh = new THREE.InstancedMesh(geometry, material, count); const matrix = new THREE.Matrix4(); for (let i = 0; i < count; i++) { matrix.setPosition( Math.random() * 10 - 5, 0, Math.random() * 10 - 5 ); instancedMesh.setMatrixAt(i, matrix); } scene.add(instancedMesh);

按需加载则是根据用户视角或交互来动态加载模型。比如一个展厅场景,用户走到哪个展位才加载哪个展位的模型。这需要配合空间划分和加载队列来实现,复杂度较高,但效果显著。

5. 那些年我踩过的 glb 加载坑

5.1 模型加载后全黑:法线与材质问题

这是最常见的问题。模型加载后一片漆黑,或者只有轮廓没有细节。原因通常有三个:法线方向反了、材质没有正确加载、光照不足。

法线问题在 Blender 里可以通过“网格 → 法线 → 重新计算外侧”来修复。如果导出后仍然有问题,可以在 Three.js 里给材质设置side: THREE.DoubleSide来临时验证是不是法线问题。

材质问题多半是贴图路径不对。glb 是自包含的,贴图应该内嵌在文件里。如果导出时选择了“分离”模式,贴图会变成外部文件,加载时就会 404。解决方法是导出时选择 glb 格式,确保贴图内嵌。

光照问题则是因为 PBR 材质需要环境光照才能正确显示。只加方向光是不够的,还需要环境贴图或者半球光。最简单的做法是用RoomEnvironment生成一个环境贴图:

import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js'; const pmremGenerator = new THREE.PMREMGenerator(renderer); const envTexture = pmremGenerator.fromScene(new RoomEnvironment()).texture; scene.environment = envTexture;

5.2 动画不播放或播放异常

动画不播放的原因可能是:导出时没勾选动画、动画名称冲突、混合器没更新。先检查 glb 文件里有没有动画数据,可以用gltf.animations打印出来看。如果有动画但不播放,检查mixer.update(delta)是否在渲染循环里被调用了。

动画播放异常(比如抖动、变形)通常是骨骼缩放或帧率问题。glTF 动画默认是线性插值,如果 Blender 里用了贝塞尔插值,导出后可能变成线性,导致动画节奏不对。解决方法是导出时选择“采样动画”,让导出器把贝塞尔曲线采样成线性关键帧。

5.3 移动端加载崩溃与内存溢出

移动端浏览器对内存的限制比桌面端严格得多。一个在桌面端流畅运行的 50MB 模型,在移动端可能直接导致页面崩溃。除了前面说的减面和纹理压缩,还有一个技巧是分块加载:把大模型拆成多个小模型,按需加载和卸载。

另外,移动端的 GPU 对纹理尺寸有限制,通常最大支持 4096x4096。如果贴图超过这个尺寸,会被自动降采样,可能导致显示异常。导出前把贴图限制在 2048 以内是比较稳妥的做法。

6. 从 model-viewer 到自定义渲染:不同场景的技术选型

不是所有项目都需要从零写 Three.js 代码。根据项目复杂度和团队技术栈,有几种不同的技术选型。

6.1 model-viewer:零代码嵌入 3D 模型

如果只是想在网页上展示一个可旋转、可缩放的 3D 模型,model-viewer 是最省事的方案。它是一个 Web Component,只需要一行 HTML 标签就能嵌入模型。

<model-viewer src="/models/my-model.glb" alt="3D 模型展示" auto-rotate camera-controls shadow-intensity="1" environment-image="neutral" style="width: 100%; height: 500px;"> </model-viewer>

model-viewer 内置了加载进度、AR 模式、相机控制、环境光照,几乎不需要写 JavaScript。缺点是定制性差,如果你需要复杂的交互逻辑或者自定义渲染效果,它就不够用了。

6.2 Three.js 自定义渲染:灵活但成本高

Three.js 适合需要深度定制的场景,比如自定义着色器、后处理特效、复杂的交互逻辑。代价是开发成本高,需要处理渲染循环、资源管理、性能优化等一系列问题。

我的建议是:如果项目只是展示模型,用 model-viewer;如果需要交互和定制,用 Three.js;如果团队有游戏引擎背景,可以考虑 Babylon.js,它的功能更全面,但学习曲线也更陡。

6.3 选型对比表

方案上手难度定制性适用场景包体积
model-viewer极低低简单展示、电商小
Three.js中等高交互展示、可视化中等
Babylon.js较高极高复杂 3D 应用大
原生 WebGL极高极高特殊需求极小

7. 坐标系与单位:glTF 里最容易混淆的部分

坐标系问题是 3D 开发中最容易出错的地方,没有之一。Blender 用 +Z 向上,glTF 用 +Y 向上,Three.js 也用 +Y 向上,但有些 GIS 或 CAD 软件用 +Z 向上或者东北天坐标系。如果不统一,模型就会躺倒、旋转错误、位置偏移。

7.1 Blender 与 glTF 的坐标转换

Blender 导出 glTF 时,导出器会自动做 +Z 到 +Y 的转换。具体来说,Blender 的 X 轴对应 glTF 的 X 轴,Blender 的 Y 轴对应 glTF 的 -Z 轴,Blender 的 Z 轴对应 glTF 的 Y 轴。这个转换是自动的,你不需要手动旋转模型。

但如果你在 Blender 里已经手动旋转了模型来适配某个坐标系,导出后就会双重转换。所以最好的做法是:在 Blender 里始终用默认的 +Z 向上建模,导出时让导出器处理转换。

7.2 东北天坐标系与 ENU 转换

有些行业应用(比如地理信息、无人机、自动驾驶)使用东北天坐标系(ENU:East-North-Up)。ENU 的 X 轴指向东,Y 轴指向北,Z 轴指向天。这和 glTF 的 +Y 向上、+Z 向前不一致。

如果模型来自 ENU 坐标系,加载到 Three.js 后需要做旋转:绕 X 轴旋转 -90 度,把 Z 轴转到 Y 轴方向。或者更简单的方法是在导出前就在 Blender 里旋转好,导出时让导出器做标准转换。

// 如果模型来自 ENU 坐标系,加载后旋转 model.rotation.x = -Math.PI / 2;

7.3 单位缩放的实际计算

假设你在 Blender 里用厘米建模,一个 100 厘米高的模型,导出 glb 后,在 Three.js 里就是 100 单位高。如果 Three.js 场景的相机远平面是 1000,那这个模型还能看到;如果远平面是 100,模型就超出视野了。

解决方法是统一单位。要么在 Blender 里把单位设成米,要么在 Three.js 里加载后缩放。缩放的公式很简单:目标单位 / 源单位。比如厘米转米,缩放 0.01;毫米转米,缩放 0.001。

// 厘米转米 model.scale.setScalar(0.01);

8. 把 glb 用起来:几个真实场景的落地思路

8.1 电商产品展示:360 度查看与 AR 试放

电商场景下,glb 模型主要用于产品 360 度展示和 AR 试放。model-viewer 的auto-rotate和camera-controls能直接满足 360 度查看需求,AR 模式则通过ar属性开启,在支持的设备上可以把模型放到真实环境中。

关键点是模型要足够轻量,移动端加载时间控制在 3 秒以内。贴图用 1024 或 2048,网格面数控制在 5 万以内,文件体积控制在 5MB 以内。

8.2 数字孪生与工业可视化:大场景的加载策略

数字孪生场景通常包含大量设备模型,每个模型可能都有精细的结构。这种场景下,不能一次性加载所有模型,需要按区域、按层级、按需加载。

一个实用的策略是:把场景按空间划分成多个区块,每个区块一个 glb 文件。用户视角进入哪个区块,就加载哪个区块的模型。离开后卸载,释放内存。配合 LOD 策略,远处用低模,近处用高模。

8.3 教育演示与交互课件:动画与交互的结合

教育场景下,glb 模型通常需要配合动画和交互。比如一个机械原理演示,需要模型能拆解、能旋转、能高亮特定部件。这需要在前端做射线检测和交互逻辑。

const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); renderer.domElement.addEventListener('click', (event) => { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(model.children, true); if (intersects.length > 0) { const clickedMesh = intersects[0].object; // 高亮或显示信息 clickedMesh.material.emissive = new THREE.Color(0x333333); } });

射线检测的性能和模型面数相关,面数越多检测越慢。对于复杂模型,可以先用包围盒做粗检测,再用精确网格做细检测。

9. 工具链与资源:从建模到上线的完整流程

9.1 Blender 插件推荐

Blender 本身功能已经很强,但几个插件能显著提升效率。glTF Export是内置的,不用额外装。Node Wrangler能简化材质节点操作。Auto Mirror和LoopTools对建模很有帮助。如果要做批量导出,可以写 Python 脚本调用 Blender 的命令行接口。

blender --background --python export_script.py

这个命令可以在无界面模式下运行 Blender 并执行导出脚本,适合集成到自动化流程里。

9.2 模型资源网站与格式转换

找现成模型的话,Sketchfab、TurboSquid、CGTrader 都有大量 glb 格式的模型。下载后如果格式不对,可以用 Blender 做转换:导入原格式,导出 glb。注意导入时检查缩放和朝向,导出时按前面说的参数设置。

9.3 上线前的检查清单

模型上线前,我通常会过一遍这个清单:

  • 文件体积是否在目标范围内(移动端 < 5MB,桌面端 < 20MB)
  • 面数是否合理(移动端 < 5 万,桌面端 < 50 万)
  • 贴图尺寸是否超标(最大 4096,推荐 2048)
  • 动画是否正常播放
  • 材质在目标光照环境下是否正常显示
  • 在目标设备上是否流畅运行
  • 加载进度和错误处理是否完善

这个清单看起来简单,但每次都能帮我提前发现几个问题。尤其是材质和光照,在开发机上看着正常,到了用户设备上可能完全不一样。

提示:上线前一定要在真实的中低端设备上测试,模拟器的性能和真实设备差距很大。我吃过好几次亏,开发机上丝滑流畅,用户手机上卡成幻灯片。

10. 我个人在实际项目中的几点体会

做了这么多 3D 展示项目,我最大的体会是:模型质量决定下限,加载策略决定上限。一个优化良好的 5MB 模型,比一个没优化的 50MB 模型体验好十倍。而加载策略,包括进度提示、错误处理、按需加载、LOD,才是让用户觉得“流畅”的关键。

另一个体会是,不要迷信工具。model-viewer 很方便,但遇到定制需求就抓瞎;Three.js 很灵活,但什么都得自己写。选型的时候先想清楚项目需要什么,再决定用什么。很多时候,一个简单的 model-viewer 加几个 CSS 动画,就能满足需求,没必要上 Three.js。

最后,坐标系和单位问题一定要在项目初期就统一。我见过太多项目因为坐标系不统一,后期改得痛不欲生。Blender 建模用米,导出用 glb,加载用 Three.js,这条链路走通了,后面就顺了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询