Three.js学习笔记
关键词:
地球、飞线、旋转、粒子、瓦片地图、交互
关联学习Unreal Engine
UE的渲染,在开源界是全球顶尖的,因此Three.js中也有不少内容借鉴自UE引擎。
比如:
UnrealBloomPass特效参考自UE的Bloom
ShaderChunk的bsdfs参考自UE 2013年的Siggraph论文Real Shading in Unreal Engine 4这篇论文
因此2者结合起来学习,是效率更高的。
学习方法
一定要摒弃错误的学习方法:盲目的看,效率极低,最好的方式还是先上个课,刻意练习下,然后在工作中实践,然后再看书深入了解原理。
不要贪多,选一个还行的课程,坚持跟着练习完,就可以入门了。不要在选课程上一直耗费时间,不要总是半途而废的学习。
找一些开源项目阅读拆解,积累经验教训和技巧;同时结合Three.js源码进行原理上的理解。
在线课程:
https://www.udacity.com/course/interactive-3d-graphics--cs291
Bruno Simon的入门课程,讲得比较浅,不过确实适合入门,$95:
THREE.Scene
场景
THREE.Mesh
网格
提到网格,就不得不提Geometry和Material,一个基本的立体物品,一般都是由Mesh+Geometry+Material这三者构成的。
- 网格(
Mesh)对象可以理解为用一种特定的材质(Material)来绘制的一个特定的几何体(Geometry)。材质(Material)和几何体(Geometry)可以被多个网格(Mesh)对象使用。比如在不同的位置画两个蓝色立方体,我们会需要两个网格(Mesh)对象来代表每一个立方体的位置和方向。但只需一个几何体(Geometry)来存放立方体的顶点数据,和一种材质(Material)来定义立方体的颜色为蓝色就可以了。两个网格(Mesh)对象都引用了相同的几何体(Geometry)和材质(Material)。
THREE.CSS3DObject
THREE.Object3D
THREE.Vector3
THREE.TrackballControls
THREE.Group
组的概念,可以将物体加入一个组,然后同时控制这一批物品的移动变换。
THREE.Box3
three-orbitcontrols
控制交互的,比如旋转、缩放等。
RenderTarget
法线
垂直于面的线
如何绘制2D图形
搞不定光线,就看不到,就是瞎蒙。
关键是THREE.Shape和THREE.ShapeGeometry,通过Shape、Path和Curve相关的API来帮助我们在3D场景中绘制出我们想要的图形。
https://blog.csdn.net/qq_30100043/article/details/78808725
Three.js自身似乎没有绘制矩形的API,我可以通过画线,封装常用的plot函数,比如Rect、Circle等。具体函数和参数可以参考ZRender。
https://juejin.im/post/6844903847198982152
继承关系

Demo代码
https://threejs.org/docs/scenes/geometry-browser.html#PlaneBufferGeometry
度量单位
Standards in anything help work together.
Having a recommended unit system just makes it easier to exchange assets between three.js applications, or convert assets from another engine to three.js.
Adopting SI units for all ThreeJS physical quantities? · Issue #6259 · mrdoob/three.js · GitHub
Three.js默认是无单位的,因此需要我们自己遵循一个单位规则。
比如Unity,其长度单位就是1米。
特别是你需要用到物理引擎的时候,遵循规范就尤为重要了,否则你要做很多特殊的处理。物理引擎大多都是用的1米作为单位长度。
其他软件的单位规范
Unreal Engine 4: 1 Unreal Unit = 1 CM.
Unity 3D: No official unit system, but it is recommended to use 1 Unity Unit = 1 Meter if you want to use physics.
Softimage: 1 Softimage Unit = 10 CM.
Clara.io: 1 Clara Unit = 1 Meter.
3DS Max: Various units, which causes a lot of conversion work.
SI Units
SI Units = The International System of Units,国际单位制。
My recommendation is to recommend standard SI units for all quantities:
- 1 Meter for distances.
- 1 Meter/Second for velocities.
- 1 Meter/Second^2 for accelerations.
- 1 Kilogram for mass.
- 1 Lumen for light intensity when using physically based lights.
启动程序
进入three.js目录:
1 | |
http://127.0.0.1:8000/study/model/girl.html
如果是调试官方的demo,则进入git/three.js项目,根目录起个服务来调试。
纹理
- 纹理很耗费性能和内存
- 纹理可以复用
- 纹理的加载有专门的loader,且可以获取到进度
材质
混合模式
支持的混合模式类型:
https://threejs.org/docs/?q=MeshPhongMaterial#api/en/constants/Materials
这里有各种混合模式的效果示例:
https://threejs.org/examples/#webgl_materials_blending
抗锯齿
这个文章介绍得不错:
https://zhuanlan.zhihu.com/p/58546367
不同类型抗锯齿的效果对比:
http://www.lanlanwork.com/blog/?post=6106
后期处理:
https://threejs.org/manual/?q=%E5%A4%84%E7%90%86#zh/post-processing
透明效果
https://stackoverflow.com/questions/15994944/transparent-objects-in-threejs/15995475#15995475
注意:如果要设置材质为透明,必须同时设置这2个属性:
transparent: true
opacity: xx
渲染器透明
将渲染器的alpha设置为true即可。
注意设置.alpha=true的时候,不要设置.background属性的值,或者通过.setClearColor()方法设置threejs背景颜色。
http://www.yanhuangxueyuan.com/doc/Three.js/WebGLRendererAlpha.html
深度
深度冲突(z-fighting)
深度冲突又叫(z-fighting)是图形渲染中一个非常常见的现象。造成深度冲突的主要原因是两个三角面片靠的非常近,在渲染的时候 gpu 很难分清到底哪个面在前,哪个面在后,从而形成闪烁(flickering)的现象
这个文章介绍得很详细:
https://zhuanlan.zhihu.com/p/151649142
如果出现闪烁,可以考虑这样设置:
配置深度测试(depthTest、depthWrite)
1 | |
即关闭depthWrite,但是开启depthTest
经过我在3D汽车产业链中的尝试,这样设置可以缓解透明物体的闪烁问题,但是并不能根治。我是这样设置的:
1 | |
polygonOffset
这个我尝试了,也没效果:
1 | |
renderOrder
最终通过给Mesh对象设置renderOrder解决了这个问题(不需要设置depthWrite和depthTest了):
1 | |
(TODO)分层设置renderOrder
将透明物体加入另外一个layer,设置这个layer的renderOrder
这个方案还没尝试过,待验证。
这个文章介绍了一些经验:
- depthWrite: false
- depthTest: false
- transparent: true
- alphaTest: 0.5
- nearPlane : 100
- renderOrder (It works, but is has many other problems. Not the correct solution)
深度测试(DepthTest)
这个答案讲解了什么是深度测试:
简而言之就是z坐标,确定物品绘制的先后顺序。
摩尔纹问题
threejs摩尔纹镜头拉远模型贴图出现摩尔纹,纹理贴图闪烁异常解决办法_红叶920的博客-CSDN博客_threejs 摩尔纹

摩尔纹和阴影有关联,是由于光源的光照导致的。
比如关掉物体材质的阴影属性,就不会出现摩尔纹了:
1 | |
或者将光源设置为不投射阴影,也能解决。
光源的shadow.camera的几个属性值设置越大,条纹越宽。
如果想要在支持阴影的情况下解决摩尔纹问题,可以这样处理:
方案一:将相机的position参数设置小一些,将模型scale同步缩小
方案二:设置光源的shadow.mapSize.width和shadow.mapSize.height属性(但是这个设置大了,又会出现弯曲的摩尔纹):
1 | |
终极方案-亲测有效!
参考这个文章:three.js学习笔记(十三)——真实渲染_hongsir_12的博客-CSDN博客
可以看到汉堡包表面有些奇怪的条纹,这种情况被称为“阴影失真shadow acne”
在计算曲面是否处于阴影中时,由于精度原因,阴影失真可能会发生在平滑和平坦表面上。
而现在在汉堡包上发生的是汉堡包在它自己的表面上投射了阴影。因此我们必须调整灯光阴影shadow的“偏移bias”和“法线偏移normalBias”属性来修复此阴影失真。
bias通常用于平面,因此不适用于我们的汉堡包。但如果你有在一块平坦的表面上出现阴影失真,可以试着增加偏差直到失真消失。normalBias通常用于圆形表面,因此我们增加法向偏差直到阴影失真消失。
1 | |
(TODO)闪烁的黑块问题

这是做3D汽车产业链遇到的,经过排查后,发现居然和相机位置远近有关系。
这个问题,和深度似乎不相关,设置renderOrder或者depthWrite/depthTest是没用的。会不会和shadow有关系?
出现问题时我们的模型缩放比例是1,相机设置参数比较大:
1 | |
后来我们将模型缩放比例调整为:
1 | |
相机参数调整成:
1 | |
问题就解决了。
性能与内存释放
https://threejs.org/docs/#manual/zh/introduction/How-to-dispose-of-objects
通过WebGLRenderer.info查看所有的元素信息。
如何调用/切换动画
位移
考虑到这个涉及的几何知识比较多,且应用场景也很多,因此单独开个大分类说一下。
自转
公转
如何制作3D地图模型
应用场景
以数据展示为主的大屏,应用3D的需求很少,像我们的行情大屏,用2D图表会更好。
3D主要用于两个分类:地理、形象化、交互
地理
比如查看股民的地理分布情况、机房预警、楼层监控之类的
形象化
比如我们的股民画像,最终需要一个人物形象,并且能够动起来,这个就适合用3D
交互
大屏都是放在那里不动的,因此交互对我们来说,是个伪需求。
文字(3D)
https://threejs.org/examples/#webgl_geometry_text
镜面倒影
上面的文字就有镜面倒影的效果。
实际上是额外创建了一个文字纹理,将其的y值设置为参照物的y的负值,初始的rotation.x设置为和参照物180度:
1 | |
这里要特别注意group这个概念,将本体和倒影绑定在一起进行交互:
1 | |
TODO:倒影的灰色是怎么处理的呢?
地球
比想象中简单,地图就是:几何形状-球( THREE.SphereGeometry ) + 材质-图片( THREE.MeshBasicMaterial )
然后通过坐标转换,再画一个浮层到页面上,就能实现3D地球+飞线效果了
这里也有一个React+Three.js画地球的文章,清晰明了,待验证。
但是这个Demo是没有地图的细节交互的,即放大缩小、详细地点的展示等等。
这个文章则讲到了坐标转换、创建光柱等。
另外,感觉Three做的地球,始终差点意思,而Unity3D似乎可以做得非常炫酷:
https://blog.csdn.net/zouxin_88/article/details/85687484
平面地图
是用Three.js的这个几何对象画的:ExtrudeGeometry(挤压缓冲几何体)
飞线
1、将线条分为N个线段,控制其中某个线段的颜色
2、用着色器,可以一个元素搞定
参考:
https://blog.csdn.net/towrabbit/article/details/103117002
https://www.jianshu.com/p/eb90d1296c2b
这个是不用Shader,用画线取点+移动物体来实现的:
https://blog.csdn.net/yy729851376/article/details/124449580
标记打点
通过贴图来实现,比如一个圆环的图片,通过设置div不断缩放,实现动态散点效果
粒子
粒子效果是动画、切换的核心,是最能提升视觉效果的,需要重点关注下。
明暗
发光
还是看three.js官方这个吧:
https://unpkg.com/three@0.120.1/examples/jsm/postprocessing/UnrealBloomPass.js
居然是参考自虚幻引擎:
https://docs.unrealengine.com/en-US/Engine/Rendering/PostProcessEffects/Bloom/index.html
下方的可供参考,也可以不看了。
https://bugjia.net/200219/278010.html
用shader?
这个文章提到通过透明度来实现:
https://zhuanlan.zhihu.com/p/38548428
1 | |
但是这个是针对单个物体,和我们想要的关系图每个线发光好像又不一样?
Three.js有自发光属性:
https://blog.csdn.net/u014291990/article/details/91461333
发光球体:
https://blog.csdn.net/sinat_30352293/article/details/78331293
瓦片地图
待尝试:
https://blog.csdn.net/zhgu1992/article/details/96191545
物理效果
结合Cannon.js来实现:
https://blog.csdn.net/weixin_43081805/article/details/88672290
一句话经验
Three.js适合做应用;如果想深入原理学习WebGL,可以看下uber的luma.gl.
开发的时候,在页面上把xyz三个轴画出来,帮助定位分析。
开发的时候,选择一个小一点的简单模型/LowPoly模型,加快加载速度。
datav.aliyun.com上有地理经纬度数据,比较新
法线贴图解决三角形性能问题
形状共用解决性能问题
用BufferGeometry替代Geometry优化性能
故障排查流程
你花了几个小时亲手建了一个堪称杰作的模型,现在你把它给导入到网页中—— 哦,天呐~😭它导入以后完全失真了、材质贴图丢了、或者说整个模型完全丢失了!
接下来我们来按照下面的步骤排除故障:
- 在Javascript的Console中查找错误,并确定当你调用*.load()的时候,使用了onError*回调函数来输出结果。
- 请在其它的应用程序中查看3D模型。对于glTF格式的模型来说,可以直接在下面的应用程序中进行查看: three.js和 babylon.js。 如果该模型能够在一个或者多个应用程序中正确地呈现,请点击这里向three.js提交Bug报告。 如果模型不能在任意一个应用程序里显示,我们强烈鼓励你向我们提交Bug报告,并告知我们你的模型是使用哪一款应用程序创建的。
- 尝试将模型放大或缩小到原来的1000倍。许多模型的缩放比例各不相同,如果摄像机位于模型内,则大型模型将可能不会显示。
- 尝试添加一个光源并改变其位置。模型或许被隐藏在黑暗中。
- 在网络面板中查找失败的纹理贴图请求,比如说C:\Path\To\Model\texture.jpg。载入贴图时,请使用相对于模型文件路径,例如 images/texture.jpg —— 这或许需要在文本编辑器中来对模型文件进行修改。
我自己还琢磨出一个办法:将一个你确定可见、可用的纯色方块,和你想要测试的模型,一起加载到场景中去,这样可以通过纯色方块来判断是不是素材设置有误导致的测试模型看不见。
常见问题
查看已经废弃的内容
可以通过src/Three.Legacy.js查看每个舍弃的类是在哪个版本废弃掉的。不过这里列出来的也是很有限的几个,全面的还是要自行搜索确认。
Geometry的faces属性不存在:这是因为125版本去掉了Geometry:
https://stackoverflow.com/questions/67989801/coloring-faces-of-a-three-js-boxgeometry
125版本之后获取顶点vertex的方法
1 | |
98版本移除了CanvasRenderer,现在全部需要转换到WebGLRenderer
https://twitter.com/mrdoob/status/1057648198762881024
2018年10月31日就做了这个修改了。
学习资料
【精】必看-各种经验合集:
The Big List of three.js Tips and Tricks! | Discover three.js
【精】海上小岛,和我们的3D目标策略很相似,比我们多了天空、水面、彩虹、镜头光晕:
Three.js 打造缤纷夏日3D梦中情岛 🌊 - Dragonir - 博客园
【精】三体中的降维打击(这个真的是超出我的想象空间了,居然Three.js能这么用!):
https://www.bilibili.com/video/BV1Sf4y147J9
https://github.com/KikiLetGo/DimReduce
用技术实现科幻中的场景,这个很适合可视化!特别是宇宙天体轨迹模拟
这里有个归一化的降维打击:https://www.bilibili.com/video/BV1tf4y1m7cx
【精】做一个FPS游戏-SimonDev:
I Tried Making an FPS Game - YouTube
物理引擎采用ammo.js,据其描述坑比较多
枪械模型通过sketchfab下载
子弹通过ammo.js的raycasting确定击中什么物体的什么位置
子弹击中的效果,是通过shader写的,用了three.js的decal system
界面UI(比如血条)是直接用HTML+CSS写的,可以设置倾斜角度,很华丽
敌人的模型是从 https://quaternius.com/ 下载的(好东西,免费游戏资产网站)
用shader的noise给敌人搞了个防护罩
关卡用立方体程序化生成,可以随机升降,nice;另外用shader给立方体增加了材质效果,很赞
通过shader给场景增加bloom效果
加了一个天空盒
用shader加了一个景深虚幻效果,屌爆了!
代码还没上传github,过一阵去看看:
simondevyoutube (simondevyoutube) / Repositories · GitHub
【这人真叼!】这里还有个人做的FPS,场景和音乐很不错,音乐提升太大了:
Three.js WebGL Game Jamir - 8 minute gameplay - YouTube
这是在线版本:JAMIR - Online sci fi browser FPS Game
Web的渲染效果,真的是一言难尽啊
视频课程:
【精】做一个个人主页房子-8小时教程:
Create a Room Portfolio with Three.js and Blender | [Awwwards' Sites Recreated] - YouTube
使用手册:
https://threejs.org/manual/#zh/fundamentals
【精】tutorial:
可以看下这个人的github(居然有程序化地形生成):https://github.com/simondevyoutube?tab=repositories
将游戏领域的东西搬到Web上,这个过程中就大有所为了。
【精】仿魔兽世界的MMORPG游戏:https://www.youtube.com/watch?v=IptkgFOoci0
实时多面折射:
Real-time Multiside Refraction in Three Steps
3DRPG,写实风格(作者居然是位老大爷,课程评价不大好),是个付费课程:https://www.youtube.com/watch?v=PVDslewhzqY
http://www.yanhuangxueyuan.com/Three.js/
【精】第一人称摄像机:https://www.youtube.com/watch?v=oqKzxPMLWxo
天空盒(Skybox)构建3D场景:https://www.youtube.com/watch?v=cp-H_6VODko
操控人物动画(有限状态机):https://www.youtube.com/watch?v=EkPfhzIbp2g
画个房子:https://zhuanlan.zhihu.com/p/442225752
这个文章底部有好资料:https://zhuanlan.zhihu.com/p/40908828
工程化开发利器-Svelte-Cubed:https://zhuanlan.zhihu.com/p/440444473
https://svelte-cubed.vercel.app/
公司同事写的3D组件:http://software.myhexin.com/gitlab/datav/3dproject.git
羡澈的电子书:https://www.ituring.com.cn/book/1272
个人兴趣爱好者(搞了RayData的效果):https://www.zhihu.com/people/de-yi-3-36/posts
郭隆邦的中文学习网站:http://www.yanhuangxueyuan.com/
(TODO)画地球的教程:https://stripe.com/blog/globe
付费课程,包含了地图、飞线: https://segmentfault.com/ls/1650000011368036?utm_source=banner
官方给的links: https://threejs.org/docs/index.html#manual/en/introduction/Useful-links
Three.js官方文档:https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene
Three.js可视化教程:http://davidscottlyons.com/threejs-intro/#scene-required
WebGL中文网:http://www.hewebgl.com/
Three.js基础:https://threejsfundamentals.org/
3D在线课程:https://classroom.udacity.com/courses/cs291/lessons/68866048/concepts/1066907330923
3D效果示例:https://davidscottlyons.com/
中文教程: https://sq.163yun.com/blog/article/203590884053413888
Three.js论坛: https://discourse.threejs.org/
StackOverflow: https://stackoverflow.com/tags/three.js/info
阿里双11大屏技术演进: https://baijiahao.baidu.com/s?id=1564991102406360&wfr=spider&for=pc
【精】概念和模型讲解: https://blog.csdn.net/weixin_43081805/article/details/88672290
【精】有个人给鸿星尔克写了一个720°全景看鞋展厅:https://zhuanlan.zhihu.com/p/394009149
用Three.js打造一个炫酷登录界面(讲得很细致全面):https://juejin.cn/post/7020571868314730532
Vue+Three加载人物模型:https://zhuanlan.zhihu.com/p/333615381
寻路系统(导航网格):
https://github.com/donmccurdy/three-pathfinding
(精)光线追踪库:
GitHub - pmndrs/react-three-lgl: 🔆 A React abstraction for the LGL Raycaster
我的世界:
https://www.youtube.com/watch?v=qpOZup_3P_A
https://github.com/danba340/minecraft-freecodecamp
3D资产
下载HDRI(高动态范围图像(High Dynamic Range Image)、Texture、Model:
从上面这个网站下载的环境贴图是一张HDRI图片,可以使用这个工具HDRI-to-CubeMap将HDRI转化为立方体贴图
做Web3D的公司
https://www.littleworkshop.fr/
做医疗的,人体脏器可视化: