本文是关于 three.js 系列文章的一部分。第一篇文章是 three.js 基础。 如果你还没有读过它,建议先从那里开始。
Three.js提供了多种类型的材质(material)。它们定义了对象在场景中的外型。你使用哪种材质取决于你想达到的目的。
有2种方法可以设置大部分的材质属性。一种是在实例化时设置,也就是我们之前看到的。
const material = new THREE.MeshPhongMaterial({ color: 0xFF0000, // 红色 (也可以使用CSS的颜色字符串) flatShading: true, });
另一种是在实例化之后再设置
const material = new THREE.MeshPhongMaterial(); material.color.setHSL(0, 1, .5); // 红色 material.flatShading = true;
注意,THREE.Color
类型的属性有多种设置方式。
material.color.set(0x00FFFF); // 同 CSS的 #RRGGBB 风格 material.color.set(cssString); // 任何 CSS 颜色字符串, 比如 'purple', '#F32', // 'rgb(255, 127, 64)', // 'hsl(180, 50%, 25%)' material.color.set(someColor) // 其他一些 THREE.Color material.color.setHSL(h, s, l) // 其中 h, s, 和 l 从 0 到 1 material.color.setRGB(r, g, b) // 其中 r, g, 和 b 从 0 到 1
在实例化时,你可以传递一个十六进制数字或CSS字符串作为参数。
const m1 = new THREE.MeshBasicMaterial({color: 0xFF0000}); // 红色 const m2 = new THREE.MeshBasicMaterial({color: 'red'}); // 红色 const m3 = new THREE.MeshBasicMaterial({color: '#F00'}); // 红色 const m4 = new THREE.MeshBasicMaterial({color: 'rgb(255,0,0)'}); // 红色 const m5 = new THREE.MeshBasicMaterial({color: 'hsl(0,100%,50%)'}); // 红色
那么,我们就来看看three.js的几个材质。
MeshBasicMaterial
不受光照的影响。MeshLambertMaterial
只在顶点计算光照,而 MeshPhongMaterial
则在每个像素计算光照。MeshPhongMaterial
还支持镜面高光。
MeshPhongMaterial
的 shininess
设置决定了镜面高光的光泽度。它的默认值是30。
请注意,将 MeshLambertMaterial
或 MeshPhongMaterial
的 emissive
属性设置为颜色,并将颜色设置为黑色(phong的 shininess
为0),最终看起来就像 MeshBasicMaterial
一样。
既然MeshBasicMaterial
、MeshLambertMaterial
可以做到的,MeshPhongMaterial
也可以做到,那为什么还要有这3种材质呢?原因是更复杂的材质会消耗更多的GPU功耗。在一个较慢的GPU上,比如说手机,你可能想通过使用一个不太复杂的材质来减少绘制场景所需的GPU功耗。同样,如果你不需要额外的功能,那就使用最简单的材质。如果你不需要照明和镜面高光,那么就使用 MeshBasicMaterial
。
MeshToonMaterial
与 MeshPhongMaterial
类似,但有一个很大的不同。它不是平滑地着色,而是使用一个渐变图(一个X乘1的纹理(X by 1 texture))来决定如何着色。默认使用的渐变图是前70%的部分使用70%的亮度,之后的部分使用100%的亮度,当然,你可以定义你自己的渐变图。这最终会给人一种2色调的感觉,看起来就像卡通一样。
接下来是2种基于物理渲染(Physically Based Rendering)的材质。Physically Based Rendering通常简称为PBR。
之前提到的材质使用简单的数学来制作,看起来是3D的,但它们并不是现实世界中实际存在的东西。2种PBR材质使用更复杂的数学来接近现实世界中的实际情况。
第一个是 MeshStandardMaterial
。MeshPhongMaterial
和 MeshStandardMaterial
最大的区别是它们使用的参数不同。MeshPhongMaterial
有一个参数用来设置 shininess
属性。MeshStandardMaterial
有2个参数用来分别设置 roughness
和 metalness
属性。
在基本层面,roughness
是 shininess
的对立面。粗糙度(roughness)高的东西,比如棒球,就不会有很强烈的反光,而不粗糙的东西,比如台球,就很有光泽。粗糙度的范围从0到1。
另一个设定,metalness
,说的是材质的金属度。金属与非金属的表现不同。0代表非金属,1代表金属。
这里是 MeshStandardMaterial
的一个快速示例,从左至右看,粗糙度从0到1,从上至下看,金属度从0到1。
MeshPhysicalMaterial
与 MeshStandardMaterial
相同,但它增加了一个clearcoat
参数,该参数从0到1,决定了要涂抹的清漆光亮层的程度,还有一个 clearCoatRoughness
参数,指定光泽层的粗糙程度。
这里是和上面一样的按 metalness
划分的 roughness
网格,但可以设置 clearcoat
和 clearCoatRoughness
。
各种标准材质的构建速度从最快到最慢:MeshBasicMaterial
➡ MeshLambertMaterial
➡ MeshPhongMaterial
➡
MeshStandardMaterial
➡ MeshPhysicalMaterial
。构建速度越慢的材质,做出的场景越逼真,但在低功率或移动设备上,你可能需要思考代码的设计,使用构建速度较快的材质。
接下来的3种材质有特殊用途。ShadowMaterial
用于获取阴影创建的数据。我们还没有介绍过阴影。等到我们介绍的时候,我们会使用这个材质来看看其背后的原理。
MeshDepthMaterial
渲染每个像素的深度,其中处在摄像机负近端面的像素其深度为0,处在摄像机负远端面的像素其深度为1。使用这个属性可以实现一些特殊效果,这在之后我们会再讨论。
MeshNormalMaterial
会显示几何体的法线。法线是一个特定的三角形或像素所面对的方向。MeshNormalMaterial
会绘制视图空间法线(相对于摄像机的法线)。x 是红色,
y 是绿色, 以及
z 是蓝色,所以朝向右边的东西是粉红色,朝向左边的是水蓝色,朝上的是浅绿色,朝下的是紫色,朝向屏幕的是淡紫色。
ShaderMaterial
是通过three.js的着色器系统来制作自定义材质。RawShaderMaterial
则是可以用来制作完全自定义的着色器,不需要three.js的帮助。这两个材质涉及的话题都很广,我们后面会讲到。
大多数材质都共享一堆由 Material
定义的设置。所有的设置都可以在文档中找到,但我们先来看看两个最常用的属性。
flatShading
:对象是否使用平面着色,默认为false
。
side
:要显示三角形的哪个面。默认值是 THREE.FrontSide
,其他选项有 THREE.BackSide
和 THREE.DoubleSide
(正反两面)。Three.js中,大多数3D对象可能都是不透明的实体,所以不需要绘制反面(面向实体内部的面)。设置 side
的最常见的原因是用于绘制平面或其他非实体对象,在这些对象中通常会看到三角形的反面。
下面是用 THREE.FrontSide
和 THREE.DoubleSide
绘制的6个平面。
关于材质,真的有很多需要考虑的地方,其实我们还有一堆东西要去做。特别是我们几乎忽略了纹理,它为我们提供了大量的选择。在我们介绍纹理之前,我们需要休息一下,介绍一下如何设置你的开发环境。