关于three.js:👋-和我一起学Threejs初级篇4-掌握纹理

🛎 本文为《和我一起学 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 🤔 思考题

  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 🤔 思考题

  1. 建议您应用之前谈及的技巧,亲手试验各种过滤器,并在评论区总结您的察看后果。

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. 💰 反对创作

您有很多形式能够表白您喜爱这篇文章,并违心反对我继续创作,例如:

  • 点击各类平台「喜爱」按钮;
  • 将文章转发在各类您喜爱的平台,并为它写一份简短的举荐语;
  • 在评论区留言;
  • 关注我的集体公众号「前端乱步」;

无论您抉择哪一项,我都会因为您的观赏而感到愉悦。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理