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
比如导入项目里的一个
模型:
在这里插入图片描述
打印会看到
数组中存在几段已有的动画片段:
在这里插入图片描述
AnimationClip 是 threejs 里内置的 动画片段 类,一个 clip 就代表一段动画
动画代表的是某个物体的动画,所以需要使用一个 动画播放器(AnimationMixer) 将动画与物体关联起来
然后使用 AnimationMixer 将 AnimationClip 转为一个 动画行为(AnimationAction) ,调用 AnimationAction 的 play 方法,同时在每一帧中更新动画播放器即可实现动画的播放
这是官方文档中给的使用示例:
在这里插入图片描述
在我们项目中,参照示例代码可以写成下面这样的动画实现:
在这里插入图片描述
在这里插入图片描述
除了基本的动画外, 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 材质的物体加上环境贴图后,他就会自己具有一定的亮度了

调整环境强度

目前场景中有两个物体,一个是模型,一个是内置的几何体,我们已经通过设置 scene 的 enviromentMap 给他们加上了环境贴图,但是他们看起来”太暗了”
我们可以通过给场景添加外部光源来提亮,但是在这种情况下,我们也可以通过设置物体材质的 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 Exponent 即 RGB指数 ,这个 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...