type
Post
status
Published
date
Mar 12, 2026
slug
summary
Texture
Material(材质)
Text
tags
Three.js
category
技术学习笔记
icon
password
Texture
即纹理,可以实现给材质添加上图片等功能
声明时要通过创建一个
TextureLoader 来加载,如:Copy
背后的原理:
Copy
这就是材质加载出来的样子:

在这里插入图片描述
如果使用
textureLoader 来加载,则不需要显式地调用 texture.needsUpdate = true 来通知纹理更新,纹理会在加载完成后自动地渲染出效果LoadingManager
可以用来监听纹理、字体等物体的加载节点,内部实现了
、
、
、
的回调函数

uvmapping(待补充)
uv坐标是一组用来定位纹理位置的坐标,掌握这个属性很重要
mipmapping

在这里插入图片描述
看左上角的图,一个4x4的正方形可以由4个2x2的正方形构成,一个2x2的正方形可以由4个1x1的正方形构成
mipmapping 是一种GPU的计算策略,内部会将一张纹理图不断地拆解成若干个小单元,最后叠加起来构成纹理,同时这样也使得渲染性能得以提升mipmapping 会对纹理图进行持续的计算直到裁剪到 1*1 的像素为止,所以,使用纹理图时,要确保纹理图的宽高为2的幂次方,如256、512、1024等,否则在 threejs 内部还要额外进行处理,浪费性能结合
mipmapping 使用的是 minFilter 属性和 magFilter 属性,一般设为 NearestFilter 性能会比较好,锐利度也够高( why? )当我们把
minFilter 设置成 NearestFilter 后,可以将 texture.generateMipmaps 设为 false 进一步提升性能使用纹理图时,纹理图大小不要太大,以降低请求的时间
Material(材质)
要在场景中渲染几何体,需要同时给这个几何体添加相应的材质才可实现
创建
时,通过配置
属性来添加纹理,比如添加一张门的纹理:

几何体正常情况下会渲染成这样:

会发现纹理图不是想象中的大小,门的两侧有同色的拉伸效果,如果想要取消掉拉伸效果,可以提供一个
材质,比如:

要求提供一张黑白图,图中白色部分会被渲染,黑色部分会被省略,配置后的效果:

注意,同时还需要配置
transparent:true 才会生效MeshNormalMaterial
法线材质使用纹理时可以使用带方向的纹理图

几何体的每个面都有n个顶点,可以通过
widthSigments 和 heightSigments 来设置,每个面的顶点由相交线相交得来,有多少个顶点就有多少个方向,这种材质可以实现打光,折射和反射;拿灯光举例,之所以能看到几何体的某个面,是因为那个面的法线恰好指向我们自己或光源的方向,而其他非正面的方向就有不同程度的阴影或看不到(背面) ,这就是
gpu 计算出 3D 物体的光形态的原理比如配置一个默认的
,效果如下:

假如加上一个
flatShading 配置,如:Copy
可以看到表面变得不光滑了:

可以再进一步开启
观察构成几何体的平面,可以看到它们的表面就是由若干个三角形构成的


在这里插入图片描述
所以
flatShading 的意义在于仅对这些平面进行着色,忽略平滑的计算效果MeshMatcapMaterial
这个材质会自动根据提供的纹理图选择合适的颜色进行渲染,选择的依据是基于对灯光的模拟,比如下图:

添加到几何体上后:
Copy

在这里插入图片描述
这个纹理的好处在于可以直接使用图片来模拟灯光、阴影的效果,比如再来一个例子:


在这里插入图片描述
一个找
matcap 纹理的仓库: github.com/nidorx/matcaps ,商用时需要确保自己有授权也可以自己用3d绘制软件画一个球,做好灯光投射等配置后,输出矩形的
matcap 图来使用MeshDepthMaterial
深度材质,特点是离摄像机越近则越白,越远则越黑


在这里插入图片描述

在这里插入图片描述
需要注意的是,灯光不会作用于该材质上,所以使用了该材质后,
ambientLight 、 pointLight 都不会有效果MeshLambertMaterial
对场景中使用
LambertMaterial ,需要同时加上光源:Copy

在这里插入图片描述
因为有点光源的存在,当把摄像机拉到背面时,可以看到阴影

这是一个会受光影响的材质,表现力很强,性能好,但是当你放大到足够大时,几何体表面会有一些模糊的线条:

如果不添加光源,比如ambientLight,看到的物体就是一团黑
MeshPhongMaterial
相比于
Lamber ,他不会有模糊的问题,自带“光反射”,但是性能不好,如果项目中出现了很多物体,导致整体性能不够好时,要考虑换掉这种材质所有可以光反射的材质都可以设置
shininess (光反射强度),( lambert 不行,因为它不会对光做出反射)使用
Phong 时, shininess 用来控制反射强度,值越大,光反射越集中:
:

用来控制光反射的颜色,比如设为红色时:

MeshToonMaterial
拥有类似卡通效果的材质,与lambert材质有类似的效果,但是由于颜色会发生急剧的变化,所以会有类似卡通的效果出现

在这里插入图片描述
从点光源的方向照射过来,到阴影的位置急剧变化,所以出现了类似断层的现象,看起来就和卡通的效果相似
一般可以配置
属性来控制它颜色的效果,比如加载一张只有深灰、浅灰、白色的渐变图作为
:

代码:
Copy
效果:

只是简单的设置
gradientMap 后会让卡通效果消失,这是因为 gpu 会自动使用 mipmapping 进行计算, mipmapping 会把渐变色计算的非常精细,所以过渡效果就会很平滑,当像素被拆分得足够精细时,颜色急剧变化就不存在了可以配置
minFilter、magFilter 为 NearestFilter 来恢复急剧的变化:Copy

在这里插入图片描述
这里是使用了只有3种颜色的渐变图片,当换成有5个渐变色时:

效果:

同时,因为使用了
NearestFilter ,所以可以把 generateMipmaps 设为 false 来进一步减少性能开销MeshStandardMaterial
这个材质与
Lambert 和 Phong 类似,但是它呈现的效果就更贴近现实,因为它的算法比较”真实”,并且参数的设置上更利于我们理解默认情况下的效果:

可以看到默认情况下
和
分别是0和1,当将他随意改变时,会发现比较神奇的效果:

官方文档对
的解释,
可以理解成
:

的解释:

去到官方文档,可以看到相关的描述:1. 基于PBR渲染,显得更真实;2. 占用、计算成本更高;3. 使用时尽可能制定envMap

在使用该材质的情况下同时加上之前的纹理图,会出现更逼真的效果,比如:
Copy

在这里插入图片描述
使用aoMap(AmbientOcclusion)
同时,为了更好地模拟阴影效果,可以引入
来实现,在官方文档中地描述:(全名:
)

比如使用下面这张
纹理图:

Copy
文档中要求第二组
uv 坐标,可以这么实现:Copy
配置完成后,可以得到下面的效果,

调整
,可以看到更深的阴影:

displacementMap
位移贴图,官方文档的解释:

当使用下面这张图当作
时:

图片的黑色部分代表其高度不受影响,从黑色到白色,高度的变化程度不断增高
得到的效果:

位移贴图可以用来呈现”高度“的效果,而”高度“的效果由几何体的顶点数量决定,拿中间的
planeGeometry 举例,打开 wireFrame ,当配置的 widthSegments 和 heightSegments 参数为10时:Copy
它的顶点相对比较少:

此时如果配置上高度图,可以看到实际上就是顶点的位置发生了变化:

以此得到了类似高度的效果;而进一步将
widthSegments 、 heightSegments 设置成更高的值时,顶点也随之增多:那么顶点突起的部分则更加精细:Copy

在这里插入图片描述
关闭
wireFrame ,对比10和100的情况,会发现顶点越多,出来的效果就越好,这是合情合理的:10的情况:

100的情况:

但是显然,我们不需要那么“高”的突起,所以可以通过
displacementScale (位移比例)来设置突起的程度,比如将 displacementScale 设为0.1时,效果就已经很好了:Copy

在这里插入图片描述

在这里插入图片描述
metalnessMap与roughNessMap
两张图,分别作为
和
:


在这里插入图片描述
代码配置上之后:
Copy

在这里插入图片描述
乍看没什么变化,但是移动一下视角会发现,表面的反射和光泽更加逼真了:

normalMap
同样的,加上一个
纹理图:

Copy

在这里插入图片描述
乍看没什么变化,但是转换一下视角会发现,细节一下就出来了:

放大则看的更明显:

同样的,也可以配置
来调整法线纹理的比例:

最后,要实现一扇完美的门,再加上之前的
alphaMa 即可:Copy

在这里插入图片描述
使用envMap
可以在这个网站找高清环境贴图:
https://hdri-haven.com/
然后去这个网站将图片转为 cubeMap 6张图: https://matheowis.github.io/HDRI-to-CubeMap/使用
时,要使用具有6个方位图片的环境贴图纹理,需要使用
来加载纹理,比如加载下面这六张图:

Copy
加载环境贴图时,
的路径是有顺序要求的:

加载环境纹理后,使用下面的配置:
Copy
得到的效果:

Text
实际使用时可以直接复制
threejs/examples 里的 typeface 字体和 LICENSE 到自己的项目目录下使用或者你有自定义的一套字体,将其在这个网站转换为
typeface 格式字体:
https://gero3.github.io/facetype.js/不过需要确保你有使用对应字体的授权
加载字体时,需要使用
fontLoader 加载字体,使用 TextGeometry 创建字体(拉伸)集合体Copy
写入上面的配置后,可以得到下面的结果:

curveSegments和bevelSegments会控制几何体顶点的数量,为了更好的性能,可以开着wireFrame来不断将它们调低,找到能满足自己需求的最小值
- 将视角放大,会看到在原点处,H字母的底部线是斜的,这是因为开启了
bevelEnabled的原因

那如果我们想把字体“居中”,该怎么做呢?
正常来说,每个几何体都有一个固定的
来将其包裹:

默认情况下,
threejs 的几何体是用球状框包裹的,此时无法通过 textGeometry.boundingBox 获取到它的边框值;
如果我们想获取到某个几何体的边框,需要先调用 textGeometry.computedBoundingBox() 方法,将几何体外边框转成 矩形边框 ,然后才可以获取到该 矩形边框 :Copy

在这里插入图片描述
这一步骤是全程不可见的,没有什么办法来肉眼看到它的边框是怎样的
所以,居中实际上就是通过将几何体所有顶点沿着x轴、y轴、z轴移动其外层盒子框体最大
x 、 y 、 z 值的一半来实现居中,主要通过 translate 方法来实现:Copy

在这里插入图片描述

在这里插入图片描述
但是移动后
box 的最大最小值貌似不太对,按理说最小值的x应该和最大值的x之和为0,但是加上会发现它们有 0.02 的差距这个差距对应的就是上面配置的
bevelSize: 0.02 ,所以实际上精确的计算方式应该是:Copy
当然,除了上面这种居中的方法,我们还可以使用
textGeometry.center() 方法来一步到位,不需要搞得这么麻烦:Copy
一个移动的问题
注意,如果是自己计算,实际上有两种办法可以“居中”,但他们略有不同
一种是将几何体通过
translate 方法修改所有顶点的位置
一种是对 mesh.position 进行修改Copy
虽然居中的效果相同,但是当要进行旋转、缩放时,结果可能就会偏离预期了
如果是移动几何体顶点的方式或
.center() 的方式,缩放、旋转都是以原点(0,0,0)进行:
在这里插入图片描述
但如果是通过移动
mesh 来实现的话,其几何体中心并不是(0,0,0),缩放、旋转的中心是以原本的中心位置为基点进行的:
在这里插入图片描述
未命名文章