🛎 本文为《和我一起学 Three.js 系列》「高级篇」的第六篇文章,文中的示例代码基于往期文章的代码进行扩大,在入手实际前,请确保您曾经在本地创立好了引入 three.js 的古代前端开发环境。
工夫过的真快!明天(2023.3.27),我终于依照当初每周更新一篇文章的打算,实现了《和我一起学 Three.js 系列》「高级篇」的 6 篇「无条件公布」的文章。每篇文章我都花了大量的工夫收集材料,从 0 开始编写代码(以保障您操作的后果和我雷同)以及思考如何让文章更易于了解,截止目前,您应该曾经能够在 3D 场景中增加各种平面物品并与之交互,您还应该理解到纹理的不同品种,以及它是让简略的 3D 模型变为事实物品的一把要害钥匙 🔑。
而本章节,咱们不仅会让上一章所学没有徒劳,还会更进一步,向您展现 3D 世界又一个激动人心的概念:「材质(Materail)」,通过学习本章节的内容,您将有能力串联起来之前所有的常识,打造一个充斥趣味的 3D 世界!
话不多说,让咱们开始吧 🤜 !
0. 系列文章合集
- 《👋 和我一起学【Three.js】「高级篇」:0. 总论》
- 《👋 和我一起学【Three.js】「高级篇」:1. 搭建 3D 场景》
- 《👋 和我一起学【Three.js】「高级篇」:2. 把握几何体》
- 《👋 和我一起学【Three.js】「高级篇」:3. 把握摄影机》
- 《👋 和我一起学【Three.js】「高级篇」:4. 把握纹理》
- 您以后在这里 👉 《👋 和我一起学【Three.js】「高级篇」:5. 把握材质》
- 🚧《👋 和我一起学【Three.js】「高级篇」:6. 把握光照》(🚨 本篇文章将于 2023.4.3 更新并反对公众号内付费观看,将在全系列文章总赞数 >= 500 时解锁公布)
- 🚧《👋 和我一起学【Three.js】「高级篇」:7. 把握暗影》(🚨 本篇文章将于 2023.4.10 更新并反对公众号内付费观看,将在全系列文章总赞数 >= 1000 时解锁公布)
- 🚧《👋 和我一起学【Three.js】「高级篇」:8. 死记硬背,神功小成》(🚨 本篇文章将于 2023.4.17 更新并反对公众号内付费观看,将在第 7,8 章节解锁后公布)
1. 什么是材质(material)?
在 Three.js 中,「材质(material)」是用来定义物体外观的属性。它蕴含如何渲染物体的信息,如色彩,光照,反射等等。材质能够被赋予不同的属性,以便实现各种不同的外观成果。
咱们上一章所提到的「纹理(Texture)」是材质的一种属性,它能够被用来在物体外表增加图像,图案,色彩等。纹理能够用来模仿各种不同的外表个性,例如木头、金属、石头等等。
为了区别「材质」和「纹理」的概念,咱们能够设想一件木质家具。该家具的「材质」是木头,但它的外观可能会因为在木外表上利用不同的纹理而产生不同的成果。例如,一个家具制造商可能会在木外表上利用一层润滑的漆,使其看起来更加润滑;另一个家具制造商则可能会在木外表上利用一个毛糙的纹理,使其看起来更加人造。同样地,在 Three.js 中,咱们能够通过利用不同的纹理来扭转物体的外观,以实现咱们想要的成果。
💡 在 Three.js 中,材质被用于为几何体的每个可见像素着色,这一过程的算法实现被称为「着色器(shader)」(一种在 GPU 上运行的程序,它用于计算几何体的每个像素的色彩和外观,能够对材质进行定制化的着色和光照计算,以及其余各种成果)。
至此,咱们能够将之前一张形容 Three.js 中创立 3D 对象的类关系图更新为上面这样:
2. 材质的通用属性
不晓得您是否还记得,咱们在《👋 和我一起学【Three.js】「高级篇」:1. 搭建 3D 场景》一文中提到过应用材质的办法:实例化一个材质类型对象,并将实例化后的对象作为第二个参数传递给网格(Mesh)对象。
const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshBasicMaterial()
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
💡 咱们能够创立一个材质后反复利用于多个几何体,这将有利于晋升利用性能。
但材质的用法可不止这些,本章咱们将逐渐解锁更多材质的应用办法。咱们将先来看看材质的一些通用且罕用的属性,而后在介绍材质的品种时,再针对特定材质介绍一些非凡的属性。
2.1 纹理配置属性
咱们上一次用了一整章内容介绍了纹理的概念,但并没有向您具体介绍该如何应用它。当初是时候了,答案也很简略,应用材质上的一系列「纹理 map
」 属性:
const geometry = new THREE.SphereGeometry(0.5, 64, 64);
const material = new THREE.MeshStandardMaterial({
map: colorTexture,
});
material.metalness = 0.8;
material.roughness = 0.8;
geometry.setAttribute(
"uv2",
new THREE.BufferAttribute(geometry.attributes.uv.array, 2)
);
material.displacementMap = heightTexture;
material.displacementScale = 0.03;
material.alphaMap = opacityTexture;
material.transparent = true;
material.opacity = 0.75;
material.normalMap = normalTexture;
material.aoMap = aoTexture;
material.aoMapIntensity = 2;
material.roughnessMap = roughnessTexture;
material.metalnessMap = metalTexture;
上面是 Three.js 中简直所有 map
属性所定义纹理及用处的信息:
2.1.1 map
属性(_十分罕用_)
用于增加「色彩纹理(Color Texture
)」(也称为反照率纹理(Albedo Texture
)),给物体外表增加图案,细节或者其余色彩纹理。
2.1.2 specularMap
(比拟罕用)
用于增加「镜面高光纹理(Specular Texture
)」,调整物体的反射和高光成果,加强物体的光照成果。
2.1.3 normalMap
(比拟罕用)
用于增加「法线纹理(Normal Texture
)」,给物体外表增加细节和深度感。
2.1.4 displacementMap
(_不罕用_)
用于增加「高度纹理(Height Texture
)」,使物体外表产生凸起或凸起的成果。
2.1.5 alphaMap
(比拟罕用)
用于增加「透明度纹理(Alpha Texture
)」实现通明成果。
2.1.6 emissiveMap
(比拟罕用)
用于增加「自发光纹理(Emissive Texture
)」,实现物体外表的自发光成果,使物体更具备光泽感。
2.1.7 envMap
(比拟罕用)
用于增加「环境纹理(Environment Texture
)」,实现反射和折射成果。
2.1.8 aoMap
(比拟罕用)
用于增加「环境光遮蔽纹理(Ambient Occlusion Texture
)」,调整物体外表的暗影和遮蔽成果。
2.1.9 roughnessMap
(比拟罕用)
用于增加「粗糙度纹理(Roughness Texture
)」,调整物体外表的光滑度。
2.1.10 metalnessMap
(比拟罕用)
用于增加「金属度纹理(Metalness Texture
)」调整物体外表的金属度。
⚠️ 要留神,并不是所有的材质都领有以上属性(因为不同的材质类型须要实现不同的外观成果,而不同的纹理贴图能够用于实现不同的成果。),因而尽管咱们曾经理解了所有的纹理类型,然而不同的纹理类型,只作用于对应的几种材质。理解材质和纹理之间的对应关系将是本文稍后将介绍的一大主题。
2.2 color
属性
很显然,color
属性用于指定物体的色彩,然而须要留神,与直觉不同,您须要通过实例化 THREE.Color
对象能力胜利配置:
material.color = new THREE.Color("red")
2.3 wireframe
属性
咱们之前提到过,WebGL 只会绘制三角形,而 wireframe
属性则是一个开关,用来示意一个物体是否仅应用 1px
的线勾画三角形。将咱们上一篇文章中的物体开启该属性,将会有这样的成果:
material.wireframe = true
2.4 opacity
属性
很显然,opacity
属性用来管制物体的透明度,它的取值范畴在 0
到 1
之间。若要想 opacity
值失效,您须要同时设置 transparent
属性:
materail.transparent = true
material.opacity = 0.5
如先前所说,当要应用「透明度纹理」时,您须要首先确保 transparent
属性开启。
2.5 side
属性
如果咱们旋转一个「立体(Plane)」,在肯定角度后(如下图所示),咱们会发现立体隐没了。
这是因为,默认状况下,Three.js 仅渲染物体「后面(THREE.FrontSide
)」,如果您须要让物体的反面也能在物体挪动中看到,那么您须要手动应用 side
属性定义物体的两面都要渲染:
material.side = THREE.DoubleSide
🚨 有时候,尽管您能够这么做,但并不意味着您「应该」这么做,因为渲染物体的两面就意味着至多双倍的计算资源,您须要审慎思考这种开销是否值得。
3. 罕用材质的品种
介绍完了材质的根本属性,当初是时候看看 Three.js 提供的多种材质了,截止目前(2023.3.27),官网共提供了 17
种材质供您抉择,您能够通过查阅官网文档获取更具体的信息。
👋 本文并不会向您介绍所有的 17 种材质类型,而只抉择介绍其中最罕用的 9 种。在这些材质中,应用 💡 合乎结尾的材质阐明要想胜利显示物体,须要场景中有光源。
3.1 根底材质(MeshBasicMaterial
)
「根底材质(MeshBasicMaterial
)」是 Three.js 中最根底,最简略的一种材质类型,没有高级的渲染成果,只能实现根本的立体渲染。它不会产生暗影、反射或其余高级成果,只能显示一个繁多的色彩或纹理。
通常,根底网格材质被用于测试、简略展现和疾速原型设计等场景。
它反对的纹理属性有:
map
;specularMap
;alphaMap
;envMap
;aoMap
;lightMap
;
⚠️ 您可能会好奇,如果设置了不反对的属性会产生什么,答案是这须要分状况探讨,对于
roughnessMap
这类属性,什么都不会产生,程序会主动疏忽掉这条指令,然而如果是normalMap
或displacementMap
属性,程序则会报错(因为这些属性须要通过「顶点着色器」(vertex shader
)或「片段着色器」(fragment shader
)来计算每个顶点或像素的偏移或法线方向,而MeshBasicMaterial
只有一个根底着色器(basic shader
),不能执行这些高级的计算)。因而,您应该始终确定只在材质上设置其反对的纹理属性。
上面的图像是咱们上一篇内容生成的球体以及替换为根底材质后的成果:
能够显著看到,应用根底材质的球体丢失了诸如金属光泽,透明度等细节。
3.2 深度材质(MeshDepthMaterial
)
「深度网格材质(MeshDepthMaterial
)」用于渲染模型的深度信息。它的特点是不思考光照和色彩等因素,仅仅依据模型的深度信息进行渲染。咱们通常将其用于深度测试,深度图像渲染等场景,也能够应用它创立一些特殊效果,例如 3D 深度图像渲染,水下成果,平面显示成果等。
它反对的纹理属性有:
map
;alphaMap
;displacementMap
;
下图是将咱们的球体应用深度材质替换后的成果:
为什么咱们的球体变得黑乎乎的?这是因为深度网格材质会依据对象间隔摄影机的间隔判断物体的亮堂水平。当物体的地位靠近摄影机的「近值」时,物体会被用红色着色,当物体靠近摄影机的「远值」时,则应用彩色着色。
👋 如果您恰好遗记了对于「近值」和「远值」的概念,兴许这是个适合的机会通过本系列第四篇文章《👋 和我一起学【Three.js】「高级篇」:3. 把握摄影机》进行回顾。
当初,让咱们把之前咱们设置的摄影机近值从 0.1
调整为 1
看看成果:
不出意外,物体变的更亮了。
3.3 捕获材质(MeshMatcapMaterial
)
「捕获材质(MeshMatcapMaterial
)」是 Three.js 中的一种非凡材质,它的渲染成果基于事后计算好的 Matcap(Material Capture)纹理,纹理中蕴含了外表的光照和色彩信息。它能够实现高效、轻量级的渲染成果,因为它不依赖于实时计算光照(这象征它不响应灯光,但它将在接管暗影的对象上投射暗影(并且暗影剪切无效),但它不会自我遮挡或接管暗影)。
👋 「光照」和「暗影」将是下两章咱们的主题,您当初不用深究,有一个大抵的印象即可。
它反对的纹理属性有:
map
;alphaMap
;displacementMap
;normalMap
;bumpMap
;
Matcap 纹理通常是一个球形的图像,其中每个像素代表了一个特定的外表法线方向和对应的色彩。在渲染过程中,物体外表的法线信息被用于从 Matcap 纹理中查找对应的色彩。上面是一张 Matcap 纹理:
下图是在咱们的球体上利用该纹理后的样子,即便咱们关掉所有光源的设置,物体仍然会有明暗的光影信息:
3.4 法线材质(MeshNormalMaterial
)
「法线材质(MeshNormalMaterial
)」会将顶点法线向量映射到 RGB 色彩空间中,通过将 X、Y、Z 重量映射到 R、G、B 通道中来示意法线方向。换句话说,它会依据物体外表的法向量信息,将每个像素的法向量转换为色彩值,而后将这个色彩值作为像素的最终色彩。
💡 法向量(normal vector)是指垂直于曲面或几何体外表的向量。在三维图形学中,法向量通常用于示意几何体外表的朝向和曲面的方向,是渲染器计算暗影、反射、光照等高级渲染成果的重要根底。
法线材质通常用于调试和可视化几何模型的法线信息,例如调试模型的外表是否失常、判断模型是否对光源反射正确等,也能够用于创立一些形象的艺术成果,例如模仿陆地、云彩等场景。您能够在这个网站浏览更多应用法线网格材质创立的数字艺术图像。
法线网格材质反对的纹理属性有:
displacementMap
;normalMap
;map
;
上面的图像是将球体替换为法线材质后的成果:
法线材质有一个额定值得注意的属性 flatShading
,它用于管制材质的平滑水平。当 flatShading
属性值为 true
时,示意应用「立体着色」(flat shading
);为 false
时,示意应用「润滑着色」(smooth shading
)。
💡「立体着色」是一种简略的着色形式,它将一个面上所有像素的色彩值设为该面法向量的色彩值,不思考像素之间的平滑过渡。这种着色形式在模型外表蕴含较多平坦区域时比拟实用,能够缩小不必要的计算,进步渲染效率。
💡「润滑着色」是一种简单的着色形式,它会在模型外表像素之间进行插值计算,使得渲染后果更加实在和真切,但也会减少计算量,影响渲染效率。这种着色形式实用于模型外表蕴含大量细节和曲面的场景。
flatShading
属性的利用场景取决于模型外表的几何形态。如果模型外表蕴含大量平坦区域,应用立体着色能够进步渲染效率;如果模型外表蕴含大量细节和曲面,应用润滑着色能够取得更加实在和真切的渲染成果。
该属性不仅能够利用于法线材质,还能够利用于咱们稍后会介绍的规范材质,兰伯特材质等高级材质中。
3.5 💡 兰伯特材质(MeshLambertMaterial
)(💡 须要光照!)
「兰伯特材质(MeshLambertMaterial
)」是一个能够响应光线的材质(🚨 这意味着当您应用该材质时,如果场景中没有光源,您将无奈看到物体!),一种用于非光洁外表的资料,因为它不存在镜面高光。
💡 Lambertian 模型基于外表法向量和光线方向的角度来计算光照成果。它是一种简化的光照模型,不波及镜面反射,因而更加高效且易于计算。
兰伯特材质常常用于性能无限的设施上,模仿磨砂外表的物体。
它反对的纹理属性有:
map
;specularMap
;alphaMap
;aoMap
;displacementMap
;envMap
;normalMap
;emissiveMap
;lightMap
;bumpMap
;
上面的图像是咱们上一篇内容生成的球体替换为根底网格材质后的成果:
👋 为了让您失去和我一样的成果,我须要想您提前剧透一下我对光源的设置:
const ambientLight = new THREE.AmbientLight(0xffffff, 10);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight("blue", 1);
directionalLight.position.set(1, 2, 0);
scene.add(directionalLight);
3.6 💡 冯氏材质(MeshPhongMaterial
)
「冯氏材质(MeshPhongMaterial
)」和之前介绍的兰伯特材质十分相似,但却反对了兰伯特材质所缺失的镜面反光成果。基于冯氏光照模型进行渲染的冯氏材质联合了漫反射、镜面反射和环境光三种光照成果,能够实现更实在的光照体现。但这一方面也意味着更大的性能开销。
💡 冯氏光照模型由冯·卡尼(Bui Tuong Phong)提出。
在须要真实感渲染的场景中,冯氏材质是一个不错的抉择。
冯氏材质反对的纹理与兰伯特材质完全相同。
从下方图片能够看到,应用冯氏材质的球体成果尽管和咱们上一节应用规范材质的成果有些区别,但曾经比之前的简略材质有明显改善:
冯氏材质还有两个非凡的属性:
shininess
用于管制物体外表材质的反光成果,该值越高,外表越亮;specular
用于管制镜面反射的色彩;
material.shininess = 100
material.specular = new THREE.Color("green")
3.7 💡 规范材质(MeshStandardMaterial
)
💡 这是上一篇文章中咱们应用的材质,它具备宽泛的纹理反对度。
「规范材质(MeshStandardMaterial
)」是一种 PBR 的材质。它依据事实世界的物理光照原理对物体外表进行渲染,以实现更实在,天然的光照成果。它还能够模仿各种金属和非金属材质。
应用规范材质渲染的物体将会比之前提到的兰伯特材质和冯氏材质领有更加真切的成果,让然,这也意味着更多的性能耗费。
规范材质能够利用咱们目前介绍的所有纹理类别 🆒!
💡 PBR(Physically Based Rendering,基于物理的渲染)是一种计算机图形学中的渲染技术,它依据事实世界的物理光照原理对物体外表进行渲染。PBR 的指标是实现更实在,天然的光照成果,使渲染后果在各种光照条件下都具备一致性和可预测性。
对于 PBR 技术的更多内容,能够参考:
- Basic Theory of Physically Based Rendering
- Physically Based Rendering and You Can Too
3.8 💡 物理材质(MeshPhysicalMaterial
)
「物理材质(MeshPhysicalMaterial
)」是对规范材质的一种扩大,能够了解为「规范材质 Pro」,这意味着它同样基于 PBR 技术,有更好的显示成果以及须要耗费比规范材质还要多的计算资源。
除此之外,它还提供了蕴含透明度的泛滥属性,用于给物体增加更多细节,例如:
clearcoat
属性:一些资料(如汽车漆、碳纤维和潮湿外表)须要在另一层不规则或毛糙的外表上笼罩一个通明反射层。clearcoat
近似这种成果,无需独自应用通明外表;transmission
属性:应用.opacity
的一个限度是高度通明资料反射较少。而通过transmission
属性则能为玻璃等薄而通明的外表提供更实在的成果;reflectivity
属性:能够使非金属材料更灵便地反射光线;sheen
属性: 可用于示意布和织物资料;
让咱们将这些属性通通增加于咱们的球体之上:
material.clearcoat = 1;
material.transmission = 0.8;
material.reflectivity = 0.8;
material.sheen = 0.8;
咱们会失去上面这个更加粗劣的球体!(我专门应用了动图来体现,这是它应得的!)
3.9 💡 卡通材质(MeshToonMaterial
)
卡通材质(MeshToonMaterial
)如它的名字一样,是一种具备卡通格调材质类型,在渲染成果上,它相似于兰伯特材质的渲染成果。默认状况下,他只会对「暗影」和「光照」两个局部着色。它反对的纹理与规范材质基本相同,只是不反对 envMap
纹理。
如果应用卡通材质渲染咱们相熟的球体,会失去上面的图像:
终于,咱们介绍完了绝大多数罕用的材质类型,如您所见,不同类型的材质有不同的实用场景,而材质的灵便度在于其反对的纹理属性,以及其余属性的多少。很多时候,咱们须要依据具体情况进行取舍,到底是要「更好的性能」还是要「更好的渲染成果」?只有能本人能给出答案。
最初,咱们用一张图来总结不同材质的纹理反对状况:
由这张图中您应该能够得出以下两点论断:
- 所有材质都有
map
属性; - 须要光照的材质都有较为宽泛的纹理反对度,其中,规范材质和物理材质的纹理反对度最高;
4. 总结
在本篇文章中,我向您介绍了创立 Web 3D 物体的最初一个要害概念:「材质(Material)」,列举了它的通用属性,并展现了不同材质的渲染成果,置信您能感触到它和咱们上一篇文章中介绍的「纹理(Texture)」概念有如许严密的分割。置信您也留神到了,在咱们介绍的 9 种材质中,有 5 种都依赖场景中的光源能力被咱们「看到」。那么在 Three.js 中,如何增加不同类别的光源呢?这将是咱们下一章介绍的主题,敬请期待!
🚨 下一期文章将于 2023.4.3 更新,将在全系列文章总赞数 >= 500 时解锁公布,届时也将反对公众号内付费观看。如果您喜爱本系列文章,并想要尽早看到更新后的文章,请转发给更多敌人观看,点赞,留言。
5. 参考资料
- Three.js 官网;
6. 应用到的工具
- 截屏:Xnip;
- 屏幕录制:QuickTime Player;
- GIF 图片:GIPHY CAPTURE;
7. 💰 反对创作
您有很多形式能够表白您喜爱这篇文章,并违心反对我继续创作,例如:
- 点击各类平台「喜爱」按钮;
- 将文章转发在各类您喜爱的平台,并为它写一份简短的举荐语;
- 在评论区留言;
- 关注我的集体公众号「前端乱步」;
- …
无论您抉择哪一项,我都会因为您的观赏而感到愉悦。
发表回复