和我一起学 Three.js【初级篇】:4. 掌握纹理
本篇文章共 5953 字,最近更新于 2023 年 04 月 19 日。
感谢您一路跟随我来到这里!截止目前为止,我们应该有能力搭建一个 3D 场景,在其中添加各种官方提供的几何体,并通过使用控制器,调整摄影机位置与几何体交互。这一切看起来都还不错,但未免有些单调。所幸本章节以及下一节的内容将让我们的 3D 世界变得丰富多彩。
本章节我们会谈及 Web 3D 世界一个非常常见的概念:「 纹理」 (Texture),它将会和下一章节的「 材质」 (Materials)概念一起使我们简单的几何体变成真实世界中我们熟悉的物体!让我们一起展开这趟旅途吧!
0. 系列文章合集
本系列第 6,7,8 章节支持在我的个人公众号「前端乱步」内付费观看,将在全平台文章「点赞数」+「评论数」 >= 500(第 6 章), 1000(第 7,8 章) 时分别解锁发布。
- 《和我一起学 Three.js【初级篇】:0. 总论》
- 《和我一起学 Three.js【初级篇】:1. 搭建 3D 场景》
- 《和我一起学 Three.js【初级篇】:2. 掌握几何体》
- 《和我一起学 Three.js【初级篇】:3. 掌握摄影机》
- 您当前在这里 《和我一起学 Three.js【初级篇】:4. 掌握纹理》
- 《和我一起学 Three.js【初级篇】:5. 掌握材质》
- 《和我一起学 Three.js【初级篇】:6. 掌握光照》
- 《和我一起学 Three.js【初级篇】:7. 掌握阴影》
- 《和我一起学 Three.js【初级篇】:8. 融会贯通》
1. 什么是纹理(Textures)
也许使用纹理(Textures)的另一个名称能更加直观的反映其本质:「 贴图 」。没错, 纹理本身就是一些图片,用来覆盖在我们的几何体或模型的表面,从而使物体具备拟真的效果 。
虽然听起来简单,但是别忘了,我们不只是想让物体「看起来」像是某个现实物体,我们还希望它符合物体「在现实中的样子」,我是指在面对不同的光照环境时,物体需要有不同的反应,以及物体需要有其对应的「 质感 」。这就不是一张图片可以解决的问题了,为此,我们需要有不同类型的图片表达物体的不同属性(例如:哪里透明,哪里应该有金属光泽等等)。还需要了解在 Three.js 的世界中,如何为一个对象加载多张纹理贴图。
让我们先从认识不同的纹理开始:
2. 纹理的种类
在本章节中,我并不会向您介绍具体「贴图」的方法,因为这涉及「材质」和「光照」等稍后我们要提及的内容。本章节的用意在于让您明白纹理有哪些种类,它们长什么样子,以及在物体上会呈现什么样的效果。
如果您能够成功访问: https:// 3dtextures.me/ 这个网站,并下载其提供的免费纹理资源,您会发现,其中的一些图片会令人不明所以,它们是不同的纹理类别,有特定的用途:
图片来源于: https:// drive.google.com/drive/ folders/1tLmUWv9WocFh88XMqsexdTkMDE6R2ABg
本章节我会使用该纹理进行演示,后续将不再额外标明资源出处。
2.1 反照率纹理(Albedo Texture)
反照率纹理(Albedo Texture)又称「颜色(Color)纹理」,是一种基本的纹理类型,用于模拟物体表面的颜色和反射特性。它是最常用的纹理类型之一,也是创建逼真 3D 场景必不可少的一部分。
在 3D 渲染中,反照率是指物体表面对于不同颜色光线的反射率,通常用一张 RGB 图片来表示。反照率纹理中的每个像素都对应着物体表面上的一个点,这个点的颜色和反射特性可以由纹理像素的颜色值来确定。例如,纹理中的白色像素表示该点表面对所有颜色的光线反射率都很高,黑色像素表示该点表面对所有颜色的光线反射率都很低。
下面的图片是一张反照率纹理:
将反照率纹理应用于一个球体时,会获得这样的效果:
为了演示方便,我设置了强度为2的白色环境光以及一束蓝色直射光,并且将粗糙度与金属度设置为0.8,我想您透露这些只是为了将来您能够实现和我一样的效果,但现在您并不需要知道如何设置。
2.2 高度纹理(Height Texture)
高度纹理(Height Texture)又称为「深度纹理」。在该图像中,每个像素的灰度值被用于表示相应位置的高度或深度。它通常被用于创建几何体的表面细节,例如山峰、岩石、河流等。
通过使用高度纹理,可以在几何体表面添加细节,并模拟光的反射和折射等视觉效果。
下面的图片是一张高度纹理:
在添加高度纹理后,我们的物体将会有明显的凹凸效果:
2.3 透明度纹理(Alpha Texture)
透明度纹理(Alpha Texture)也称为「Alpha 通道纹理」,它是一种特殊的纹理,其中包含了用于控制材质透明度的 Alpha 通道数据。
Alpha 通道是图像中的第四个通道,它表示每个像素的透明度值。在 Alpha 纹理中,Alpha 通道的值被用于控制每个像素的透明度。使用 Alpha 纹理,您可以轻松地创建透明的材质效果。例如,您可以使用 Alpha 纹理来控制几何体表面的透明区域,以实现类似玻璃、水、烟雾等材质的效果。
下面的图片是一张透明度纹理:
但当我们使用透明度纹理并开启透明配置时,我们会得到这样的立方体:
让我们放大球体的一部分,可以看到更明显的透视效果:
2.3.1 思考题
- 如果您恰好先设置了透明度纹理后设置高度纹理,您可能会发现物体并没有透明效果?您知道这是为什么吗?
欢迎在评论区留言和我讨论。
2.4 法线纹理(Normal Texture)
法线纹理(Normal Texture)是一种常用的纹理映射技术,用于在三维场景中模拟表面细节和几何体形状的变化。它通常是一个 RGB 颜色纹理,其中每个像素的颜色值编码了该像素对应的表面法线方向。
在计算机图形学中,法线(normal)是一个向量,表示平面或曲面在某个点的垂直方向。
当使用法线纹理时,它可以模拟出几何体表面的微小细节,例如凹陷、凸起、皱褶等等。在渲染过程中,根据法线纹理中的像素颜色值计算每个像素的法线方向,从而在表面绘制时应用正确的光照和阴影。与其他纹理映射技术相比,法线纹理可以更加高效地模拟出复杂的细节效果,并且对于性能要求较高的场景,它通常是更好的选择。
在实际应用中,法线纹理通常用于增加几何体表面的真实感和细节,例如在建筑物、地形、人物等场景中。同时,法线纹理也可以和其他纹理映射技术如漫反射贴图、高光贴图、环境贴图等结合使用,从而创造出更加逼真的效果。
下面的图片是一张高度纹理:
当我们将法线纹理添加至我们的球体中时,我们会发现更加细腻的效果:
感觉已经像模像样了?但其实我们目前为止才只走了一半,剩下的三种纹理还可以从不同侧面(主要是如何反馈光线)增强物体的真实感。
2.5 环境光遮蔽纹理(Ambient Occlusion Texture)
环境光遮蔽纹理(Ambient Occlusion Texture)又称 AO 贴图,是一种用于计算光照阴影效果的纹理贴图。
在三维图形渲染中,环境光遮蔽(Ambient Occlusion,简称 AO)是一种近似于全局光照的技术,用于模拟光线在不同物体之间的传播和遮挡效果。通过计算光线在物体表面处的遮蔽程度,可以增强场景的真实感和细节。
环境光遮蔽贴图通常使用灰度图像来表示光线的遮蔽程度,颜色越暗表示遮蔽程度越高,颜色越亮表示遮蔽程度越低。
下面的图片是一张环境光遮蔽纹理:
让我们继续增强我们的物体,得到下面的效果:
请注意看我们立方体的暗部,它比之前有更加立体的效果。
2.6 粗糙度纹理(Roughness Texture)
粗糙度纹理(Roughness Texture)是一种用于模拟物体表面粗糙程度的纹理贴图。
在三维图形渲染中,表面的粗糙度会影响光线在其表面的反射和散射,从而影响物体的光照效果。粗糙度纹理通常使用灰度图像来表示表面的粗糙度,颜色越暗表示表面越光滑,颜色越亮表示表面越粗糙。
下面的图片是一张粗糙度纹理:
在我们的物体上继续添加粗糙度纹理,看一看到物体显得更加细腻:
2.6 金属度纹理(Metalness Texture)
金属度纹理(Metalness Texture)是一种用于模拟表面金属质感的纹理贴图。
在三维图形渲染中,金属表面的光照反应与非金属表面有很大的差异。金属表面的反射主要来自于镜面反射,而非金属表面的反射则包括漫反射和镜面反射。因此,在渲染金属表面时需要指定其金属度属性,以便正确地计算其光照反应。
金属度贴图通常使用灰度图像来表示表面的金属度,颜色越黑表示表面越非金属,颜色越白表示表面越金属。通过调整金属度贴图,可以使物体的表面更真实地反射光线,从而增强渲染效果。
下面的图片是一张金属度纹理:
让我们看看物体的完成形态:
我们的金属骨架显得非常真实,或许通过动图能够更好的观察这一点:
终于,我们了解了所有的纹理类型,并感受到纹理的力量!它让我们的 3D 物体变得更加丰富!但是在进入下一章之前,让我们再了解有关纹理的一个重要知识点:「PBR 标准」。
2.7 PBR 标准
PBR(Physically Based Rendering)是一种基于物理的渲染技术,它是一组标准和约定,用于描述和模拟真实世界中材质和照明的行为。 PBR 渲染器会使用这些标准来模拟物体表面上的反射、折射、阴影和其他光线互动效果。
PBR 的基本思想是使用真实世界中材质的物理特性来模拟材质的外观。这包括使用高光贴图、环境贴图、法线贴图等来描述材质的不同属性。
我们刚才使用的纹理都符合 PBR 标准,您可以从官网的说明中证实这一点:
通常,我们可以通过观察材质的属性来判断它是否符合 PBR 标准。符合 PBR 标准的材质通常包括漫反射、高光、法线、粗糙度、金属度等属性,而且这些属性是相对独立的,可以单独进行调整。如果材质缺少其中的某个属性,或者属性之间没有相互关联,那么它可能不符合 PBR 标准。
3. 纹理的使用方式
在掌握各种纹理类型后,接下来我们需要回到 3D 场景中,了解如何将纹理添加到几何体上。
3.1 纹理加载
3.1.1 直接加载纹理
虽然纹理就是一张图片,但是我们却不能通过图片链接直接加载,而需要实例化一个 Texture 对象。这是因为 WebGL 需要一种特殊的数据结构与 GPU 交互,例如对纹理进行一些预处理,生成纹理坐标等。
下面是在 Three.js 中正确加载一张图片的方式,看起来会稍显复杂:
const image = new Image();
const texture = new THREE.Texture(image);
image.addEventListener('load', () => {
texture.needsUpdate = true;
image.src = '...';
在 《 和我一起学【Three.js】「初级篇」:1. 搭建 3D 场景》 这篇文章中,我们介绍过在 3D 世界中创建物体的方法:
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
不知道您是否还有印象这张图片:
提示:出现在 《 和我一起学【Three.js】「初级篇」:2. 掌握几何体》 中。
您应该已经完全理解,3D 世界中的物体由「 几何体(Geometry) 」和「 材质(Material) 」构成。虽然材质将是我们下一章讨论的主题,但是现在我们可以暂时剧透一下它和纹理之间的关系:
- 从功能角度上看:材质是 更高级的概念 ,它会将多个纹理组合起来,以定义 3D 对象表面的复杂外观和材质属性。
- 从代码角度上看: 纹理是材质的一个参数 ;
因此,要为物体添加纹理,我们需要通过如下代码:
const material = new THREE.MeshBasicMaterial({ map: texture });
如下方所示,您可以看到我们的立方体已不再是从前那个单调的立方体:
我使用了 3D TEXTURES 网站的
stylized fur 02
纹理,让立方体上有很多毛确实有些古怪(虽然我觉得也未尝不可),但是您可以替换成任何您喜欢的纹理!
3.1.2 使用 TextureLoader
除了上述方法外,
TextureLoader
对象实例本身还有一个
.load()
方法,通过该方法,我们可以更优雅地获取图片加载状态并绑定对应的逻辑:
const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load(
'/textures/custom/Stylized_Fur_002_basecolor.jpg',
console.log('loading finished')
console.log('loading progressing')
console.log('loading error')
)
注意三个回调函数分别表示图片「加载成功」,「加载中」与「加载失败」三种状态。
3.1.3 使用加载管理器
当同时加载多张图片时,您可能不希望一次又一次地将相同的逻辑绑定在每一张图片上(这也违背了 DRY 原则),此时,您可以使用
LoadingManager
对象,要使用它,我们同样需要初始化一个实例:
const loadingManager = new THREE.LoadingManager()
loadingManager.onStart = () => {
console.log('loading started')
loadingManager.onLoad = () => {
console.log('loading finished')
loadingManager.onProgress = () => {
console.log('loading progressing')
loadingManager.onError = () => {
console.log('loading error')