NSDT工具推荐Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

材质的作用,简单来说,就是为几何体的每个可见像素添加颜色。但是添加颜色并不是那么简单。 它取决于光照、材料的物理特性等因素,经过一系列算法后,决定这些像素点显示什么颜色。 这个算法是写在一个叫shader shader的程序里的。Shader 编程非常具有挑战性,我们会在后面的课程中学习。

下面我们就来学习如何使用Three.js中的预设素材。

1、准备我们的场景

为了测试材质,我们首先准备一个基本场景并加载一些纹理图像。

创建 3 个不同的几何体(球体、平面和环面)并在所有 3 个几何体上使用相同的 MeshBasicMaterial。给它们不同的 x 坐标以将它们分开。场景的 add(...) 方法也可以通过传递多个参数一次性将所有几何体添加到场景中:

/**
 * Objects
 */
const material = new THREE.MeshBasicMaterial()
const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(0.5, 16, 16),
    material
)
sphere.position.x = - 1.5
const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(1, 1),
    material
)
const torus = new THREE.Mesh(
    new THREE.TorusGeometry(0.3, 0.2, 16, 32),
    material
)
torus.position.x = 1.5
scene.add(sphere, plane, torus)

然后,我们添加一些代码来让它们旋转:

/**
 * Animate
 */
const clock = new THREE.Clock()
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
    // Update objects
    sphere.rotation.y = 0.1 * elapsedTime
    plane.rotation.y = 0.1 * elapsedTime
    torus.rotation.y = 0.1 * elapsedTime
    sphere.rotation.x = 0.15 * elapsedTime
    plane.rotation.x = 0.15 * elapsedTime
    torus.rotation.x = 0.15 * elapsedTime
    // ...
}
tick()

接下来,我们加载将用于测试材料的各种纹理图像。

/**
 * Textures
 */
const textureLoader = new THREE.TextureLoader()
const doorColorTexture = textureLoader.load('/textures/door/color.jpg')
const doorAlphaTexture = textureLoader.load('/textures/door/alpha.jpg')
const doorAmbientOcclusionTexture = textureLoader.load('/textures/door/ambientOcclusion.jpg')
const doorHeightTexture = textureLoader.load('/textures/door/height.jpg')
const doorNormalTexture = textureLoader.load('/textures/door/normal.jpg')
const doorMetalnessTexture = textureLoader.load('/textures/door/metalness.jpg')
const doorRoughnessTexture = textureLoader.load('/textures/door/roughness.jpg')
const matcapTexture = textureLoader.load('/textures/matcaps/1.png')
const gradientTexture = textureLoader.load('/textures/gradients/3.jpg')

创建基础材质 MeshBasicMaterial 并将其中一个纹理图像分配给贴图(颜色贴图)。

const material = new THREE.MeshBasicMaterial({ map: doorColorTexture })

OK,准备工作就到这里,下面我们就来学习如何使用Three.js中的预设素材。

2、基础材质 MeshBasicMaterial

MeshBasicMaterial,顾名思义,确实是最“基础”的材质……

我们可以在实例化材质时传递属性参数,也可以在创建后更改这些属性:

const material = new THREE.MeshBasicMaterial({
    map: doorColorTexture
})
// Equals
const material = new THREE.MeshBasicMaterial()
material.map = doorColorTexture

我们使用第二种方法,看起来更清晰

  • map colormap 属性可以在几何体的表面映射一个纹理贴图
material.map = doorColorTexture
  • color 属性将在几何体表面设置统一的颜色。 我们可以使用各种常见的 Web 端颜色值,但是我们必须实例化一个 THREE.Color 对象。
material.color = new THREE.Color('#ff0000')
material.color = new THREE.Color('#f00')
material.color = new THREE.Color('red')
material.color = new THREE.Color('rgb(255, 0, 0)')
material.color = new THREE.Color(0xff0000)
  • 当同时使用 colormap 时,纹理贴图会受到颜色的影响。
  • 将 wireframe 属性设置为 true 将显示构成几何体的三角形网格,网格线将是 1 像素的线宽,无论与相机的距离如何。
material.wireframe = true
  • opacity 透明度属性必须与transparency 属性一起使用来设置材质的透明度。 取值范围为 0 到 1,其中 0 表示完全透明,1 表示不透明。
material.transparent = true
material.opacity = 0.5
  • 除了通过opacity属性设置材质整体的透明度,我们还可以使用alphaMap透明度贴图让材质的个别部分透明。
material.transparent = true
material.alphaMap = doorAlphaTexture
  • 3D世界中的几何体是由面构成的,而面也分为正面和背面。 默认情况下,Three.js 只会渲染正面。
  • 注意:不正对相机称为正面。
  • 并且通过设置侧面渲染属性,我们可以决定是正面渲染还是背面渲染THREE.BackSide或者两面渲染THREE.DoubleSide
  • 注意:当设置为 DoubleSide 时,性能成本较高,因为渲染了 2 倍的三角形面数。
material.side = THREE.DoubleSide

其中一些属性,例如线框和不透明度,在其他类型的材质中也有,后面不再赘述。

3、法线材质 MeshNormalMaterial

MeshNormalMaterial 可以显示漂亮的紫色、蓝色和绿色,看起来就像我们在上一节纹理贴图中看到的法线纹理贴图。 这不是巧合,因为两者都与我们所说的法线有关。

const material = new THREE.MeshNormalMaterial()

那么究竟什么是法线呢? 法线是始终垂直于平面的线,表示面的方向。 在 3D 引擎中,每个顶点都有法线信息。

由于法线表示顶点的方向,因此它们自然可用于计算如何反射或折射光。

使用 MeshNormalMaterial 时,颜色只会显示法线相对于相机的方向。 这意味着如果我们围绕球体旋转,你会看到颜色始终相同。

除了线框、不透明度等基本属性外,MeshBasicMaterial 还可以使用一个新的 flatShading 平面着色属性:

material.flatShading = true

平面着色意味着法线不会从一个顶点插值到另一个顶点。 MeshNormalMaterial 通常用来调试和观察法线信息,但是看起来很华丽,所以也可以直接用来做一些很独特的效果。

4、材质捕获材质 MeshMatcapMaterial

名字有点乱,不过Matcap确实是Material和Capture这两个词的组合,意思是材质捕捉。

这是一种很好的材料,效果很好,性能也很好。

渲染通常需要几何体、光源、材质和着色器的参与。 在matcap中,光源和材质信息在3D建模软件中直接烘焙成纹理贴图,渲染时可以直接使用。 计算量自然大大减少,性能明显提升。 我们还可以轻松地在不同的 matcap 纹理之间切换,这看起来就像切换材质一样。

使用 MeshMatcapMaterial 材质时,必须使用看起来像球体的参考纹理贴图。

材质将根据相对于相机的法线方向提取纹理贴图上的颜色。

设置材质捕捉贴图:

const material = new THREE.MeshMatcapMaterial()
material.matcap = matcapTexture

有些面看起来被灯光照亮,但实际上不需要任何光照参与计算。

但是有一个问题,因为光照和材质信息是预先烘焙到纹理贴图中的,所以无论相机的方向如何以及光线的角度如何,它看起来都是一样的。

下面我们可以尝试几种不同的matcap纹理贴图

const matcapTexture = textureLoader.load('/textures/matcaps/2.png')
const matcapTexture = textureLoader.load('/textures/matcaps/3.png')
const matcapTexture = textureLoader.load('/textures/matcaps/4.png')
const matcapTexture = textureLoader.load('/textures/matcaps/5.png')
const matcapTexture = textureLoader.load('/textures/matcaps/6.png')
const matcapTexture = textureLoader.load('/textures/matcaps/7.png')
const matcapTexture = textureLoader.load('/textures/matcaps/8.png')

网上可以找到很多matcap贴图,可以理解为开箱即用的素材,例如这里

5、深度材质 MeshDepthMaterial

MeshDepthMaterial 的外观不是由光照或某种材质决定的,而是由物体到相机的距离决定的。 当物体离相机较近时,它会呈现白色,而当它离相机较远时,它会呈现黑色。

const material = new THREE.MeshDepthMaterial()

我们可以使用这种材质来观察几何体和相机之间的距离。 我们将在以后的课程中使用它。

6、加一点光!

我们将在下面介绍的一些材质只能通过灯光才能看到,所以让我们在场景中添加两个简单的灯光。

创建环境光并将其添加到场景中:

/**
 * Lights
 */
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(ambientLight)

创建另一个点光源并将其添加到场景中:

// ...
const pointLight = new THREE.PointLight(0xffffff, 0.5)
pointLight.position.x = 2
pointLight.position.y = 3
pointLight.position.z = 4
scene.add(pointLight)

目前,我们不需要对这些灯了解太多。 后面会有专门的章节来学习更多关于灯的知识。

7、MeshLambert材质

MeshLambertMaterial 是我们将学习的第一种对光有反应的材料:

const material = new THREE.MeshLambertMaterial()

虽然它由于照明而过度着色,但如果你仔细观察球体,应该会在细节中看到一些奇怪的地方。

8、MeshPhong材质

Phong 是 1970 年代提出的渲染逼真灯光效果的算法,以作者 Bui Tuong Phong 的名字命名。

MeshPhongMaterial 就是应用这个算法的材质。 效果与MeshLambertMaterial类似,但过度明暗更自然,性能消耗略高于MeshLambertMaterial。

const material = new THREE.MeshPhongMaterial()

你可以通过亮度属性控制光的反射。 值越高,反射越强,表面越亮,看起来越抛光。 还可以使用 specular specular color 属性来更改反射的颜色:

material.shininess = 100
material.specular = new THREE.Color(0x1188ff)

这段代码使反射光有点偏蓝,看到了吗?

9、MeshToon材质

MeshToonMaterial卡通材质可以让我们的几何体呈现出二维卡通的风格,俗称3渲染2:

const material = new THREE.MeshToonMaterial()

默认情况下,我们只能看到两种颜色(一种用于深色侧,一种用于浅色侧)。 如果你想要更多的颜色过渡,你可以使用渐变 Map 属性并加载渐变 Texture:

material.gradientMap = gradientTexture

如果我们直接设置gradientMap,会发现卡通效果无效,明暗太丝滑。 这是因为我们使用的渐变纹理很小,这与我们在纹理映射部分学习的minFilter、magFilter和mipmapping有关。

解决方法也很简单,设置minFilter和magFilter为THREE.NearestFilter即可

不要忘记添加 generatempmaps=false:

gradientTexture.minFilter = THREE.NearestFilter
gradientTexture.magFilter = THREE.NearestFilter
gradientTexture.generateMipmaps = false

现在我们可以看到卡通效果有3种颜色,也可以换成5种颜色过渡的贴图:

const gradientTexture = textureLoader.load('/textures/gradients/5.jpg')

10、网格标准材料

MeshStandardMaterial标准材质采用了基于物理规则的渲染原理,也就是我们在纹理类中学习的PBR。 之所以称为标准,是因为PBR已经成为很多3D渲染引擎的标准,无论你在任何软件或引擎中使用标准材质,结果都是一样的。

与 MeshLambertMaterial 和 MeshPhongMaterial 一样,标准材质必须包含灯光、更逼真的阴影和更多参数,例如粗糙度和金属度。

const material = new THREE.MeshStandardMaterial()

我们可以直接调整粗糙度和金属度的值来观察

material.metalness = 0.45
material.roughness = 0.65

11、网格物理材质

物理材质 MeshPhysicalMaterial 是 MeshStandardMaterial 的扩展或增强版本,提供更高级的基于物理的渲染属性,例如:

  • 清漆特性:有些材料(如汽车漆、碳纤维和湿表面)需要在另一个可能不规则或粗糙的表面上涂上一层透明的反射层。 透明涂层无需单独的透明表面即可达到类似的效果。
  • Physically Based Transparency:可以实现更逼真的玻璃般的薄透明效果。
  • 更好的反射。

12、点材质

专门用于THREE.Points的材质,后面我们做粒子效果的时候会用到。

13、着色器材质

着色器材质ShaderMaterial和原始着色器材质RawShaderMaterial都可以用来创建我们自己的材质,它需要我们学习GLSL语法,暂时跳过,我们会在专门的课程中学习。

14、环境贴图

为什么环境贴图突然添加到材质部分? 因为环境贴图的作用是将周围的环境反映在几何体的表面上,所以可以结合各种材质来非常快速地构建出“真实感”。

在这里练习如何使用它会很棒。

首先,让我们像以前一样使用调试 UI 设置一个非常简单的 MeshStandardMaterial:

const material = new THREE.MeshStandardMaterial()
material.metalness = 0.7
material.roughness = 0.2
gui.add(material, 'metalness').min(0).max(1).step(0.0001)
gui.add(material, 'roughness').min(0).max(1).step(0.0001)

要将环境纹理贴图添加到材质,必须使用 envMap 属性。 Three.js 目前只支持立方体类型的环境纹理贴图。 想象自己在一个盒子里,你要反映的是盒子里面的六个面。

请在文件夹 /static/textures/environmentMap/ 中找到我为你准备的环境纹理图像

要加载环境纹理贴图,必须使用 CubeTextureLoader 而不是 TextureLoader。 注意 cubeTextureLoader.load 的参数是一个数组。
const cubeTextureLoader = new THREE.CubeTextureLoader()
const environmentMapTexture = cubeTextureLoader.load([
    '/textures/environmentMaps/0/px.jpg',
    '/textures/environmentMaps/0/nx.jpg',
    '/textures/environmentMaps/0/py.jpg',
    '/textures/environmentMaps/0/ny.jpg',
    '/textures/environmentMaps/0/pz.jpg',
    '/textures/environmentMaps/0/nz.jpg'
])

现在,我们可以在材质的 envMap 属性中使用 environmentMapTexture:

material.envMap = environmentMapTexture

尝试调整材质的金属感和粗糙度,观察不同的反射效果。


原文链接:An article to understand the materials in Three.js

BimAnt翻译整理,转载请标明出处