6️⃣threejs学习笔记(六)
2026-3-12
| 2026-3-17
字数 5840阅读时长 15 分钟
type
Post
status
Published
date
Mar 12, 2026
slug
summary
模型导入
tags
Three.js
category
技术学习笔记
icon
password

模型导入

目前比较火的模型文件格式是 GLTF 格式,它可以存储更多的数据,比如一整个场景的对象、对象嵌套、骨骼、动画等
一个 GLTF 模型的仓库: https://github.com/KhronosGroup/glTF-Sample-Assets

模型的格式

gltf 常常存在4种格式:
  • gltf
  • gltf-binary
  • gltf-draco
  • gltf-embedded
    • 在这里插入图片描述

gLTF

默认的模型格式,由 模型.gltf模型.bin模型.png 组成
.gltf 文件实际上存储的是 json 数据,其中包括摄像机、灯光、场景、材质、物体变换等信息,但是不包括 几何体纹理 信息
.bin 文件通常包含了几何体数据,比如每个顶点的位置、UV坐标、法线、颜色等信息
.png 则是纹理图片

gLTF-Binary

通常只有一个 .glb 文件,其中包含了一个模型的所有信息,它是二进制的,体积相对更小;因为只有一个文件,所以导入起来比较方便,但是想要更改其中的数据就比较麻烦了

gLTF-Draco

gLTF 本身类似,但是它 使用了 Draco 算法对缓冲数据进行压缩,所以它的体积相对于 gLTF 更小

gLTF-Embedded

也是只有一个文件,但是它相当于将 gLTF 里的所有数据放到了一个文件中(包括纹理图片),通常不会使用这种格式
综上,如果想要修改模型中的数据,最好使用默认的 gLTF 格式,如果想只是用一个文件来实现,那使用 gLTF-Binary 比较合适
目前 gLTF 格式的模型正在逐渐成为标准,它内部使用的材质通常为PBR材质(Physically Based Realistic),PBR材质也会逐渐成为标准的使用材质

导入模型

需要使用对应的 gltfLoader 来导入, threejs 本身也不带有这个 loader ,所以要从它的仓库里导入:
控制台可以看到导入成功回调里打印的模型信息:
在这里插入图片描述
现在我们要导入的是鸭子的模型,但是这个模型的位置在
的位置,除了这个模型之外,还存在一个摄像机
在这里插入图片描述
现在我们试着只将该模型导入:
导入之后发现,模型特别大 直接超出了整个场景:
在这里插入图片描述
回到控制台可以看到,其实在模型的外部
是有设置缩放的:
在这里插入图片描述
并且你会发现, children 数组里模型从 json 里的场景被添加到我们页面的场景后, json 数据里的模型就不在了
所以,我们实际情况下可以根据需要,判断是只需要导入模型还是模型原本所处的场景(毕竟场景本身也是继承自 Object3D 的,同样也可以被添加到 threejs 的场景中)
修改代码,改成导入整个场景
现在就正常了:
在这里插入图片描述
当然,也可以自己手动给模型设置缩放,导入的效果和上面一致:

Draco压缩

Draco 压缩是一种用于3D图形的数据压缩算法,主要用于3D模型的传输和存储,它由google开发而来,开源
对于一个非常大的模型,如果在浏览器主线程中进行加载或者对 draco 模型解压,可能会导致浏览器卡上几秒无法操作
所以, threejs 提供了 dracoLoader ,以此实现在 worker 中进行这部分加载,避免阻塞主线程
首先将 gltfLoader 加载的模型路径定位到 glTF-Draco 目录下
此时会发现控制台报错,没有找到
在这里插入图片描述
参照官方文档的示例,需要引入
以及设置
解码器的路径:
在这里插入图片描述
但是按照官方这样子写控制台会报错,提示找不到
的解码器:
在这里插入图片描述
很显然,因为项目中的相对路径命中的是静态文件目录 static ,所以找不到对应的解码器
所以这里需要我们去到对应的解码器目录中将整个 draco 文件夹拷贝到我们的静态目录里:
在这里插入图片描述
在这里插入图片描述
然后,将路径修改为我们的静态目录路径即可:
但是,
本身只支持
后缀的模型文件,我们的
目录下的模型文件依然是
,所以按照官方示例中的引入方式是不行的,控制台会报错:
在这里插入图片描述
所以,这里我们依然需要使用原本的 gltfLoader ,只不过为其添加一个 DracoLoader
这样子导入经过 draco 压缩的模型就没问题了
Draco 压缩并不是一个十全十美的方案,对于一个只有 100KB 的模型,经过 draco 压缩后也才仅仅缩减至50KB,为了用户少加载这 50KB 的资源,我们做了:
  1. 引入 DracoLoader
  1. draco 解码器导入到根目录
  1. 在浏览器内花费一定的时间进行解压缩
实际场景中,我们还是应该按需使用这个 性能优化 方案
如果需要对普通的模型进行draco压缩,可以通过 gltf-pipeline 工具来实现: https://github.com/CesiumGS/gltf-pipeline

动画

gltf 还可以存储模型的动画,当使用 gltfLoader 导入一个带有动画的模型时,控制台打印该模型数据会发现里面有一些 AnimationClip
比如导入项目里的一个
模型:
在这里插入图片描述
打印会看到
数组中存在几段已有的动画片段:
在这里插入图片描述
AnimationClipthreejs 里内置的 动画片段 类,一个 clip 就代表一段动画
动画代表的是某个物体的动画,所以需要使用一个 动画播放器(AnimationMixer) 将动画与物体关联起来
然后使用 AnimationMixerAnimationClip 转为一个 动画行为(AnimationAction) ,调用 AnimationActionplay 方法,同时在每一帧中更新动画播放器即可实现动画的播放
这是官方文档中给的使用示例:
在这里插入图片描述
在我们项目中,参照示例代码可以写成下面这样的动画实现:
在这里插入图片描述
在这里插入图片描述
除了基本的动画外, threejs-AnimationAction 还支持动画之间的交叉淡化过渡,可以平滑地过渡两个动画
切换动画时,需要先停止上一个动画,再启动下一个动画,否则会出现两个动画混合在一起的情况
先看直接切换的情况:
在这里插入图片描述
在这里插入图片描述
官方文档中提供的过渡方法如下:
在这里插入图片描述
所以如果要从旧动画过渡到新动画,这样写就可以了:
在这里插入图片描述
在这里插入图片描述

调试

对于模型而言,在 threejs 里用代码进行调试的效率是很低的, threejs 提供了一个 threejs/editor 在线编辑器来帮助我们调试
打开编辑器官网:
,点击顶栏的
清空整个场景,然后将模型文件夹下的文件全部选中并拖入编辑器,就可以看到对应的模型内容
在这里插入图片描述
模型默认是PBR材质的,要让他显示需要外部光源,点击顶栏的 添加 -> 光源 -> 环境光 ,然后在右侧操作面板将光的颜色改为白色,亮度也做适当地调整就可以看到正确的狐狸模型了

demo地址

https://github.com/JohnWicc/threejs-demo/tree/21-import-models

环境贴图

使用 THREE.CubeTextureLoader 进行加载,通常加载的环境贴图格式为 px、nx、py、ny、pz、nz 的六张图
通过给场景 background 设置 envMap 可以直接让背景变成类似360度环绕的盒子:
效果如图:
在这里插入图片描述
目前场景中有一个导入的模型和一个 TorusKnot 几何体,他们都是使用的 MeshStandardMaterial ,目前环境没有光照,所以他们均为黑色
通过给 scene.enviroment 设置 envMap 则可以为场景中所有的物体添加上该 envMap
这是官网的描述:
在这里插入图片描述
效果:
在这里插入图片描述
可以看到,给一个 MeshStandardMaterial 材质的物体加上环境贴图后,他就会自己具有一定的亮度了

调整环境强度

目前场景中有两个物体,一个是模型,一个是内置的几何体,我们已经通过设置 sceneenviromentMap 给他们加上了环境贴图,但是他们看起来”太暗了”
我们可以通过给场景添加外部光源来提亮,但是在这种情况下,我们也可以通过设置物体材质的 envMapIntencity 来调整其对环境贴图反应的强度,以此来提高亮度,这样才是比较好的处理方式
我们可以给每个物体单独设置 envMapIntensity ,但是如果场景里物体很多的话,一个一个设置效率就太低了。 threejs 中提供了一个 traverse 遍历方法,来实现对继承自 Object3D 类的物体遍历
接下来实现一个对场景内的物体进行遍历的函数,对每一个 MeshStandardMaterial 材质的物体设置其对环境的响应程度,并且在模型加载完成时调用他:
这样,我们就不需要添加额外光源来让整体的亮度提高了:
这样

数值调制

在代码里修改 updateAllMaterials 方法中的 envMapIntensity 数值还是不够方便,为了提高效率我们还是应该结合 lil-gui 进行调试
声明一个全局 envMapIntensity 变量,通过 lil-gui 监听该变量的修改来调用 updateAllMaterials 方法对材质的环境响应程度进行更新:

背景调制

通过 scene.backgroundBlurriness 可以设置背景的模糊程度
在这里插入图片描述
在这里插入图片描述
假如使用了一张不是很清晰的 envMap ,那么可以添加适当的模糊来让用户只关注在物体上
通过 scene.backgroundIntensity 则可以设置背景的亮度:
在这里插入图片描述
在这里插入图片描述

hdr格式文件

对于上面同样的环境贴图,
格式的文件长这样:
在这里插入图片描述
.hdr 代表的是 High Dynamic Range ,意味着其能存储更精确的颜色数据,正常的图片通常存储 R、G、B 颜色本身有哪些,但是HDR更进一步地将 R、G、B 的数值也存储了下来
所以,要在 threejs 里加载 hdr 格式的文件贴图,则需要使用 RGBELoaderRGBE 代表的是 Red Green Blue ExponentRGB指数 ,这个 loader 官方文档中是没有记述的
注意这里还需要对加载出来的
设置
,告诉
要使用什么投影算法来计算
在这里插入图片描述
假如不添加
,得到的结果就是:
在这里插入图片描述
添加之后,整体不需要调整亮度就已经很亮了:
在这里插入图片描述
可以和基本的环境贴图版本做一下对比:
在这里插入图片描述
使用 hdr 一个是基本的亮度有所提升,另一个是 hdr 格式的环境贴图割裂感不会有那么强,比如上面红色框选的部分
但是显然,随着 .hdr 文件而来的代价就是其体积较大,加载性能肯定不如 cubeTexture ,所以还是一样的要根据实际需要进行取舍

一个找高清环境贴图的网站

https://polyhaven.com/

HDR转为cube map的网站:

https://matheowis.github.io/HDRI-to-CubeMap/

自己创建环境贴图(使用blender)

从上面的案例可以看到,环境贴图除了当作一个“图”之外,它还可以创造出光源的效果,所以,除了使用已有的环境贴图,也可以自己使用 blender 等3D软件创造出一个环境贴图:
例如,现在打开
,进行如下设置: 修改右下角的渲染引擎,改为
,设置
的采样率为256(默认的是1024,但是笔者电脑没那么强,所以要适当降低)
在这里插入图片描述
然后去到输出中,将输出分辨率改为
(2的指数幂)
在这里插入图片描述
来到世界环境,在”表面”添加节点,设置世界背景为黑色(这一步是为了让整个场景的背景保持为黑色,方便看出光源的颜色)
在这里插入图片描述
然后在场景中添加一个面光,调整其为适当的大小,并放置在合适的位置:
在这里插入图片描述
在右下角的“物体”菜单,将
勾选
在这里插入图片描述
默认情况下,在 blender 中添加的光源实体在最后的渲染阶段是不会出现在摄像机内的,所以需要在灯光的可见性中进行设置
添加完一个面光后将其进行复制,在四个方位分别放置一个,并且设置光的颜色为不同的颜色,同时将这些光的强度设置为
在这里插入图片描述
最后,往场景中添加一个”全景摄像机“,选择全景类型为等距矩形,将其初始位置和旋转角度全部置为0:
在这里插入图片描述
然后就可以进行渲染了,按 F12 ,进入渲染界面,会看到最终要渲染出来的结果长这样,这就是一个360度的柱状全景图被展开成平面图的样子
在这里插入图片描述
在这里插入图片描述
和前例中的
格式文件是一样的:
在这里插入图片描述
在渲染界面按
将其保存为图片,保存时选择HDR模式存储
在这里插入图片描述
然后就可以在项目中导入刚刚获得的
文件了,在当前场景下导入我们自己的环境贴图效果为:
在这里插入图片描述
我们现在是把场景的 background 也设置了这个环境贴图,把他去掉就可以只留下场景中的两个模型,并且也同时具有光照效果了:
在这里插入图片描述
在这里插入图片描述
所以,实际上所谓的环境贴图达成的目的是模拟灯光、阴影等真实世界的效果,你的环境贴图和背景图片可以完全不同;当然你也可以自己在 threejs 里创建4个 RectLight 来实现打光的效果,但是论开发的效率和运行在网站上的性能当然就不如环境贴图了

压缩hdr

一个好的,高清的 .hdr 通常动辄几M,使用这么”大”的环境贴图对于用户的体验也是不友好的
而所谓的压缩 hdr ,实际上是将其转换为 cubeMap 格式的图片来实现环境贴图,通常这会牺牲环境光的质量换来更快的加载速度
比如对我们刚刚自己创建的
文件进行压缩,打开前文提到的网站:
上传我们的HRD文件:
在这里插入图片描述
选择
进行导出,格式选择最下面的6张图的格式:
在这里插入图片描述
并保存后,会得到一个压缩文件,将其解压就可以得到6张标准的
图片,这样就可以用
内置的
来加载了
在这里插入图片描述
加载的代码:
最终的效果:
在这里插入图片描述
效果显然没有
来的要好,并且环境贴图的质量也掉的很严重,初始的光照效果远远偏离了我们设置的
的光照强度,但是我们也可以通过修改物体材质的
来控制其亮度,修改后其实达成的光照效果也能和使用
时一致:
在这里插入图片描述

其他获取环境贴图的方式

NVIDIA CANVAS

英伟达提供的AI生成式环境贴图,简单来说就是用户随意画画,AI生成对应的场景,需要安装他们的软件
在这里插入图片描述
https://www.nvidia.com/zh-tw/studio/canvas/
这个软件目前还处于测试阶段,使用它导出的图片是 .exr 格式,这个格式的文件也是需要在 threejs 中使用对应的 loader 进行加载
在这里插入图片描述
在这里插入图片描述

AI生成skybox

AI生成的环境贴图,根据提示词进行生成,生成出来的也是柱状全景的环境贴图
在这里插入图片描述
在这里插入图片描述
不过生成后下载需要付费。。
在这里插入图片描述
网站: https://skybox.blockadelabs.com/

加载jpg

实际上柱状全景图也可以导出成 jpg 格式的图片进行加载,比如使用项目里的一张jpg格式的全景图:
在这里插入图片描述
在这里插入图片描述
threejs 中使用基本的 textureLoader 进行加载:
得到的效果和原图有点区别,这是因为 threejs 里一般的 texture 默认色彩空间是线性的色彩空间 THREE.LinearSRGBColorSpace
在这里插入图片描述
在这里插入图片描述
需要多加一句,修改色彩空间模式:
这样得到的效果就对味儿了,亮度不够的话就通过右上角调整即可:
在这里插入图片描述
在这里插入图片描述
综上所述,到目前为止,环境贴图的作用是实现打光, threejs 中的材质可以根据需要设置受光的强度,相比于在 threejs 中创造多个不同的光源,使用环境贴图来实现场景光对于性能更加友好;同时,环境贴图的格式有很多,根据不同的需求,选择不同的贴图格式也是比较重要的

使用时注意事项

  1. 如果在 mesh 被添加到场景后才加载完 envMap ,那你可能需要手动通知 material 进行更新:
  1. 如果 envMap 已经加载完成,后续再添加一个新的 mesh ,那么就需要重新调用一次 mesh.envMap = envMapTexture 的设置,否则物体不会受已经添加到场景中的 envMap 影响

demo地址

https://github.com/JohnWicc/threejs-demo/tree/enviroment-map
threeJs学习笔记(六)
  • Three.js
  • threejs学习笔记(五)threejs学习笔记(七)
    Loading...