🛎 本文为《和我一起学 Three.js 系列》高级篇的第五篇文章,文中的示例代码基于上一篇文章中的代码进行相应的扩大,请确保您曾经浏览上一篇文章,并和我一起应用 vite 搭建了古代前端开发环境。
感谢您一路追随我来到这里!截止目前为止,咱们应该有能力搭建一个 3D 场景,在其中增加各种官网提供的几何体,并通过应用控制器,调整摄影机地位与几何体交互。这所有看起来都还不错,但未免有些枯燥。所幸本章节以及下一节的内容将让咱们的 3D 世界变得丰富多彩。
本章节咱们谈判及 Web 3D 世界一个十分常见的概念:「纹理」(Texture),它将会和下一章节的「材质」(Materials)概念一起使咱们简略的几何体变成真实世界中咱们相熟的物体!让咱们一起开展这趟旅途吧!
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')
}
const textureLoader = new THREE.TextureLoader(loadingManager)
之后,再应用 textureLoader
时咱们就能够复用绑定的逻辑。当咱们须要在所有素材加载成 功后移除进度条并展现内容时,加载管理器就特地有用。
3.2 UV 开展问题
兴许您会感到纳闷,如果将纹理利用于奇怪的平面图形上,会产生什么?答案是 Three.js 会帮忙咱们以肯定的规定贴图(尽管可能不是咱们想要的)。这就波及到三维模型外表上的每个点如何映射到二维立体上的问题。这一类问题咱们称之为「UV 开展问题」。
要解决这个问题咱们须要依据模型的几何形态和拓扑关系,对模型外表进行切割和开展,以便将每个点对应的纹理坐标计算出来。对于一些几何体而言,这个过程会非常复杂,因而咱们在这里只是大略理解这个概念,并不会深刻介绍。
💡 在计算机图形学中,UV 通常指的是二维纹理坐标,用于将纹理映射到三维模型外表上。它通常用 (u, v) 示意,它们的取值范畴是 [0, 1]。在 UV 坐标空间中,(0, 0) 示意纹理图像的左下角,(1, 1) 示意纹理图像的右上角。通过调整 UV 坐标的取值,能够管制纹理在模型外表上的映射形式,实现不同的纹理成果。
3.3 操作纹理
就像在 CSS 中操作背景图片一样,咱们也能够通过相似的形式设置纹理是否反复,旋转多少度以及是否须要一些位移,上面咱们将别离看看如何实现这些性能:
3.3.1 反复
在 Three.js 中,纹理默认会被拉伸至整个材质,如果心愿反复纹理贴图,那么就须要额定告知 Three.js 如何解决纹理边缘:
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
💡 当设置为
THREE.MirroredRepeatWrapping
时代表镜像反复。
下面的代码别离设置了横向和纵向的反复模式,之后,就能够通过纹理实例对象上的 repeat
属性重复使用材质:
const texture = textureLoader.load('...')
texture.repeat.x = 2
texture.repeat.y = 3
3.3.2 旋转
通过 rotation
属性能够旋转纹理:
texture.rotation = Math.PI * 0.25
默认状况下,纹理会围绕 UV 坐标 (0, 0)
点进行旋转,如果要围绕纹理核心旋转须要应用 center
属性:
texture.center.x = 0.2
texture.center.y = 0.3
3.3.3 位移
扭转纹理位移非常简单,应用 offset
属性即可:
texture.offset.x = 0.4
texture.offset.y = 0.6
4. 纹理优化技术
这里的纹理优化是指,如何在最大化缩小计算资源的状况下,取得最好的视觉效果。为此,咱们能够别离从两个角度登程:「算法」和「资源」。
4.1 应用过滤器和 Mipmapping
Three.js 会应用一种名为 mipmapping
的纹理优化技术,它通过事后生成一系列不同大小的纹理贴图(也称为 mipmap),来缩小在渲染过程中的计算和内存耗费。
在应用 mipmapping 技术时,WebGL 将纹理图像分解成一系列递加的尺寸,从原始尺寸开始,每次放大到原来的一半,直到放大到一个像素。这些不同大小的纹理贴图被存储在 GPU 内存中,随着渲染间隔的变远,GPU 会主动抉择更小的贴图来显示,从而缩小了纹理贴图在远处的像素数进步性能。
应用 mipmapping 技术能够缩小因纹理采样而导致的失真和锯齿,进步纹理品质。同时,因为更小的纹理贴图须要更少的内存,因而也能够缩小内存占用。
而 mipmapping 技术对于开发者的意义在于,当纹理图像的分辨率大于或小于模型的分辨率时,Three.js 让开发者可能通过 API 抉择适合的过滤算法以获得计算速度与渲染成果之间的均衡。
4.1.1 放大过滤器
通过纹理上的 minFilter
属性能够配置纹理的放大过滤器,以应答纹理图像分辨率大于物体分辨率,须要放大时的成果,有如下可选值:
THREE.LinearMipmapLinearFilter
(默认):应用 MipMap 和线性插值,成果比拟平滑,然而计算速度较慢;THREE.NearestFilter
:应用最近邻插值,这种插值形式会产生显著的马赛克成果,然而计算速度比拟快;THREE.LinearFilter
:应用线性插值,成果比拟平滑,然而计算速度比较慢;THREE.NearestMipmapNearestFilter
:应用最近邻插值和 MipMap,这种插值形式会产生显著的马赛克成果,然而计算速度较快;THREE.NearestMipmapLinearFilter
:应用 MipMap 和最近邻插值,这种插值形式会产生显著的马赛克成果,然而计算速度较快;THREE.LinearMipmapNearestFilter
:应用线性插值和 MipMap,成果比拟平滑,然而计算速度较慢;
您能够这样配置:
texture.minFilter = THREE.NearestFilter
4.1.2 放大过滤器
您能够通过 magFilter
属性配置放大过滤器,它的应用场景刚好和放大过滤器相同,并且可选值也少的多,只有两个:
THREE.LinearFilter
(默认):应用线性插值,成果比拟平滑,然而计算速度比较慢;THREE.NearestFilter
:应用最近邻插值,这种插值形式会产生显著的马赛克成果,然而计算速度比拟快;
4.3 🤔 思考题
- 建议您应用之前谈及的技巧,亲手试验各种过滤器,并在评论区总结您的察看后果。
4.2 优化纹理资源
和所有优化伎俩相似,最终咱们要回到资源自身,即始终抉择满足需要的状况下,更小的资源或将资源压缩到足够小。除此之外,还须要留神因为 mipmapping 技术会二分的切割纹理,因而咱们应该始终保障纹理的宽高是「偶数」!
5. 总结
终于完结了!真是一趟漫长的旅途 🚄!心愿您感觉这是值得的。在本篇文章中,我向您介绍了 Three.js 里对于纹理的所有信息,包含纹理的类别,加载,操作形式以及一些优化伎俩,心愿那些例子能让您印象粗浅,也心愿您可能亲自动手实际。在下一篇文章中,咱们将探讨一个更高级的概念「材质」,届时咱们终将有能力发明出实在,令人惊叹的物体!请您放弃好奇,和我持续摸索,下周见 👋。
6. 参考资料
- Three.js 官网:threejs.org/;
- three.js journey:threejs-journey.com/;
- PBR 规范:https://marmoset.co/posts/basic-theory-of-physically-based-re…
- 3D 材质:http://3dtextures.me/
7. 应用到的工具
- 截屏:Xnip;
- 屏幕录制:QuickTime Player;
- GIF 图片:GIPHY CAPTURE;
8. 💰 反对创作
您有很多形式能够表白您喜爱这篇文章,并违心反对我继续创作,例如:
- 点击各类平台「喜爱」按钮;
- 将文章转发在各类您喜爱的平台,并为它写一份简短的举荐语;
- 在评论区留言;
- 关注我的集体公众号「前端乱步」;
- …
无论您抉择哪一项,我都会因为您的观赏而感到愉悦。
发表回复