• 【Three.js】第十二章 Materials 材质


    12.Materials 材质

    介绍

    材质用于为几何体的每个可见像素着色。
    决定每个像素颜色的算法属于着色器中编写的。编写着色器是 WebGL 和 Three.js 最具挑战性的部分之一,但不要担心;Three.js 有许多带有预制着色器的内置材质。
    我们将在以后的课程中探索如何创建我们自己的着色器。现在,让我们使用 Three.js 自带材料。

    Setup 设置

    启动器不包含任何对象。这是一个很好的机会来修改创建网格的基础知识。

    准备我们的场景

    为了测试材质,我们应该准备一个可爱的场景并加载一些纹理
    创建由 3 种不同几何体(一个球体、一个平面和一个环面)组成的 3 个网格体,并在所有 3 个几何体上使用相同的 MeshBasicMaterial。是的,您可以在多个网格体上使用一种材质。移动左侧的球体和右侧的环面以将它们分开。
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23


    tick我们现在可以像在动画课上那样在函数上旋转对象:

    /**
     * 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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    你应该看到你的 3 个物体在缓慢旋转。
    我们将要发现的材料以许多不同的方式使用纹理。让我们像在纹理课程中所做的那样使用TextureLoader加载一些纹理。
    所有纹理图像都位于该/static/textures/文件夹中。现在,我们将加载/static/textures/door/文件夹中的所有门纹理、/static/textures/matcaps/文件夹中的第一个 matcap 纹理和/static/textures/gradients/文件夹中的第一个渐变纹理。
    确保在material实例化之前这样做:

    /**
     * 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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    为确保所有纹理都加载良好,您可以使用属性在材质上使用它们map,正如我们在纹理课程中看到的那样。

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


    到目前为止,我们只使用了 MeshBasicMaterial 它在我们的几何体上应用了统一的颜色或纹理。
    如果您在Three.js 文档中搜索“material” ,您会发现有很多我们可以使用的类。让我们都试试看。

    网格基础材质

    MeshBasicMaterial可能是最“基本”的材质……但是还有很多我们还没有涉及的属性。
    您可以在MeshBasicMaterial实例化前,材质参数传入的对象中设置其中的大部分属性,但您也可以直接在实例上更改这些属性:

    const material = new THREE.MeshBasicMaterial({
        map: doorColorTexture
    })
    
    // Equals
    const material = new THREE.MeshBasicMaterial()
    material.map = doorColorTexture
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们将使用第二种方法,但您可以随意使用。

    map 地图

    map属性将在几何体表面应用纹理:

    material.map = doorColorTexture
    
    • 1

    color 颜色

    color属性将在几何体表面应用统一的颜色。当您直接更改color属性时,您必须实例化一个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)
    
    • 1
    • 2
    • 3
    • 4
    • 5


    结合colormap用颜色为纹理着色:

    material.map = doorColorTexture
    material.color = new THREE.Color('#ff0000')
    
    • 1
    • 2

    wireframe 线框

    无论相机的距离如何,该wireframe属性都会用 1px 的细线显示构成几何体的三角形:

    material.wireframe = true
    
    • 1

    opacity 不透明度

    opacity属性控制透明度,但要正常工作,您应该将transparent属性设置为true以通知 Three.js 此材质现在支持透明度:

    material.transparent = true
    material.opacity = 0.5
    
    • 1
    • 2

    alphaMap 阿尔法地图

    现在透明度开始工作了,我们可以使用该alphaMap属性来控制纹理的透明度:

    material.transparent = true
    material.alphaMap = doorAlphaTexture
    
    • 1
    • 2

    side 边

    side属性可让您决定面部的哪一侧可见。默认情况下,正面是可见的 ( THREE.FrontSide),但您可以改为显示背面 ( THREE.BackSide) 或同时显示背面 ( THREE.DoubleSide):

    material.side = THREE.DoubleSide
    
    • 1

    您应该看到曲面的的正面和背面。
    尽量避免使用**THREE.DoubleSide**因为渲染两侧意味着要渲染两倍以上的三角形。
    其中一些属性类似于wireframeopacity可以与其他类型的材料一起使用。我们不会每次都重复这些。

    MeshNormalMaterial 网格法线材质

    MeshNormalMaterial显示漂亮的紫色、蓝色、绿色,看起来像我们在纹理课程中看到的法线纹理这并非巧合,因为两者都与我们所说的法线有关:

    const material = new THREE.MeshNormalMaterial()
    
    • 1


    法线是在每个顶点中编码的信息,包含面部外侧的方向。如果将这些法线显示为箭头,您会得到从组成几何体的每个顶点出来的直线。

    您可以将法线用于许多事情,例如计算如何照亮面部或环境应如何在几何体表面反射或折射。
    使用MeshNormalMaterial时,颜色将仅显示法线相对于相机的方向。如果围绕球体旋转,您会发现颜色始终相同,无论您正在查看球体的哪个部分。
    虽然您可以使用我们在MeshBasicMaterial中发现的一些属性,例如wireframe、transparent、opacity和side,但您还可以使用一个新属性,称为:flatShading

    material.flatShading = true
    
    • 1


    flatShading将使面变平,这意味着法线不会在顶点之间进行插值。
    MeshNormalMaterial可用于调试法线,但它看起来也很棒,您可以像 ilithya 在她的作品集https://www.ilithya.rocks上所做的那样使用它。

    MeshMatcap材质

    MeshMatcapMaterial是一种很棒的材料,因为它看起来很棒,同时又非常高效。
    为了使其工作,MeshMatcapMaterial需要一个看起来像球体的参考纹理。

    然后材质将根据相对于相机的法线方向在纹理上拾取颜色。
    要设置该参考 matcap 纹理,请使用以下matcap属性:

    const material = new THREE.MeshMatcapMaterial()
    material.matcap = matcapTexture
    
    • 1
    • 2


    网格看起来会被照亮,并且反射光泽,但它只是一个看起来像金属的纹理。
    唯一的问题是无论相机方向如何,错觉都是一样的。此外,您无法更新灯光,因为没有灯光。
    尝试文件夹上可用的不同纹理/static/textures/matcaps/(只是下面一行之一):

    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7


    关于在哪里可以找到 matcaps 纹理,您可以像任何类型的纹理一样在网络上进行简单的搜索。如果不是供个人使用,请确保您有权使用该纹理。还有这个庞大的 matcaps 列表:https 😕/github.com/nidorx/matcaps
    您还可以使用 3D 软件创建自己的 matcaps,方法是在相机前以方形图像渲染一个球体。最后,您可以尝试在 Photoshop 等 2D 软件中制作 matcap。

    MeshDepthMaterialnear 网格深度材质

    如果MeshDepthMaterial接近相机的值, MeshDepthMaterialnear将简单地将几何体着色为白色,如果它接近far相机的值,则为黑色:

    const material = new THREE.MeshDepthMaterial()
    
    • 1

    您可以将此材质用于特殊效果,您需要知道像素与相机的距离。我们将在以后的课程中使用它。

    AmbientLight 添加灯光-- 后续的材质反射灯光

    后面几个材料需要灯光才能看到。因为材料会反射灯光了,让我们在场景中添加两个简单的灯光。
    创建一个AmbientLight并将其添加到场景中:

    /**
     * Lights
     */
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
    scene.add(ambientLight)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建一个PointLight并将其添加到场景中:

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

    在以后的课程中,我们将更多地了解灯光、它们的工作原理以及如何调整它们。

    MeshLambert光材质

    MeshLambertMaterial是我们将要使用的第一种对光有反应的材料

    const material = new THREE.MeshLambertMaterial()
    
    • 1


    如您所见,事情变得越来越现实。虽然照明不是很令人信服,但这是一个好的开始。
    MeshLambertMaterial支持与MeshBasicMaterial相同的属性,但也支持一些与灯光相关的属性。我们将在本课稍后使用更充分的材料看到这些属性。
    MeshLambertMaterial是使用灯光最高性能材料。不幸的是,这些参数并不方便,如果您仔细观察像球体这样的圆形几何体,您会在几何体上看到奇怪的图案。

    MeshPhong高光材质

    MeshPhongMaterial与MeshLambertMaterial非常相似,但奇怪的图案不太明显,您还可以看到几何体表面的光反射

    const material = new THREE.MeshPhongMaterial()
    
    • 1


    MeshPhongMaterial的性能不如MeshLambertMaterial。但是,在这个级别上并不重要。
    您可以使用该属性控制光反射**shininess**。值越高,表面越光亮。您还可以使用以下属性更改反射的颜色**specular**

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


    光反射将呈蓝色。

    MeshToon材质

    MeshToonMaterial在属性方面类似于MeshLambertMaterial,但具有卡通风格:

    const material = new THREE.MeshToonMaterial()
    
    • 1


    默认情况下,您只能获得两部分着色(一个用于阴影,一个用于光)。要为着色添加更多步骤,您可以使用该gradientMap属性并使用gradientTexture我们在课程开始时加载的:

    material.gradientMap = gradientTexture
    
    • 1


    如果您对此进行测试,您会发现卡通效果不再起作用。那是因为我们使用的渐变纹理很小,并且该纹理的像素是混合的。是的,就像我们在纹理课程中看到的那样mipmapping,这是minFilter,magFilter和的问题。
    要解决这个问题,我们可以简单地将minFilterand更改magFilterTHREE.NearestFilter.
    使用THREE.NearestFilter意味着我们没有使用 mip 映射,我们可以通过以下方式停用它gradientTexture.generateMipmaps = false

    gradientTexture.minFilter = THREE.NearestFilter
    gradientTexture.magFilter = THREE.NearestFilter
    gradientTexture.generateMipmaps = false
    
    • 1
    • 2
    • 3


    您现在应该可以通过中间步骤看到卡通效果。
    您可以使用位于以下位置的图像尝试更多步骤/static/textures/gradients.5.jpg

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

    网格标准材料

    MeshStandardMaterial使用基于物理引擎的渲染原理是的,我们正在谈论在纹理课程中看到的 PBR。与MeshLambertMaterialMeshPhongMaterial一样,它支持灯光,但具有更逼真的算法和更好的参数,如粗糙度和金属度。
    之所以称为“标准”,是因为 PBR 正在成为许多软件、引擎和库中的标准。这个想法是用真实的参数获得真实的结果,无论您使用何种技术,您都应该得到非常相似的结果:

    const material = new THREE.MeshStandardMaterial()
    
    • 1


    您可以直接更改roughnessmetalness属性:

    material.metalness = 0.45
    material.roughness = 0.65
    
    • 1
    • 2

    添加调试界面

    虽然这不是必需的,但现在是添加调试 UI 的绝佳时机。这对于测试不同的属性非常有用。
    首先,我们必须将Dat.GUI依赖项添加到我们的项目中。在终端的项目文件夹(服务器当前运行的位置)上,使用以下命令:

    npm install --save lil-gui
    
    • 1

    正如调试课程中提到的,我们正在安装lil-gui而不是 dat.gui。这个库的工作原理与 dat.gui 完全相同,我们将在课程的其余部分将其称为“dat.gui”。
    然后,在你的代码之上,导入(如果你停止了它,lil-gui不要忘记重新启动服务器):npm run dev

    import * as dat from 'lil-gui'
    
    • 1

    您现在可以创建它的一个实例:

    /**
     * Debug
     */
    const gui = new dat.GUI()
    
    • 1
    • 2
    • 3
    • 4

    并添加调整(在创建材料之后):

    gui.add(material, 'metalness').min(0).max(1).step(0.0001)
    gui.add(material, 'roughness').min(0).max(1).step(0.0001)
    
    • 1
    • 2

    就是这样。您现在可以根据需要更改metalnessroughness
    让我们继续MeshStandardMaterial的其他属性。
    map属性允许您应用简单的纹理。您可以使用doorColorTexture

    material.map = doorColorTexture
    
    • 1


    aoMap属性(字面意思是“环境遮挡贴图”)将在纹理较暗的地方添加阴影。为了让它起作用,您必须添加我们所说的第二组 UV(有助于在几何体上定位纹理的坐标)。
    我们可以像在几何课上那样简单地添加新属性并使用默认uv属性。更简单地说,我们复制了uv属性。
    调用这个新属性uv2

    sphere.geometry.setAttribute('uv2', new THREE.BufferAttribute(sphere.geometry.attributes.uv.array, 2))
    plane.geometry.setAttribute('uv2', new THREE.BufferAttribute(plane.geometry.attributes.uv.array, 2))
    torus.geometry.setAttribute('uv2', new THREE.BufferAttribute(torus.geometry.attributes.uv.array, 2))
    
    • 1
    • 2
    • 3

    您现在可以添加aoMap使用doorAmbientOcclusionTexture纹理并使用aoMapIntensity属性控制强度:

    material.aoMap = doorAmbientOcclusionTexture
    material.aoMapIntensity = 1
    
    • 1
    • 2


    裂缝应该看起来更暗,这会产生对比并增加尺寸。
    displacementMap属性将移动顶点以创建真正的浮雕:

    material.displacementMap = doorHeightTexture
    
    • 1


    它应该看起来很糟糕。这是由于我们的几何图形缺少顶点(我们需要更多细分)并且位移太强:

    material.displacementScale = 0.05
    
    // ...
    
    new THREE.SphereGeometry(0.5, 64, 64),
    
    // ...
    
    new THREE.PlaneGeometry(1, 1, 100, 100),
    
    // ...
    
    new THREE.TorusGeometry(0.3, 0.2, 64, 128),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13


    我们可以使用metalnessMapand roughnessMap,而不是为整个几何体指定 metalness and roughness

    material.metalnessMap = doorMetalnessTexture
    material.roughnessMap = doorRoughnessTexture
    
    • 1
    • 2


    反射看起来很奇怪,因为metalnessroughness属性仍然分别影响每个贴图。我们应该评论它们或使用它们的原始值:

    material.metalness = 0
    material.roughness = 1
    
    • 1
    • 2


    无论细分如何,normalMap都会伪造法线方向并在表面上添加细节:

    material.normalMap = doorNormalTexture
    
    • 1


    您可以使用该属性更改法线强度normalScale。小心,它是一个Vector2

    material.normalScale.set(0.5, 0.5)
    
    • 1

    最后,您可以使用该alphaMap属性控制 alpha。不要忘记将transparent属性设置为true

    material.transparent = true
    material.alphaMap = doorAlphaTexture
    
    • 1
    • 2


    这是一扇漂亮的门。随意调整属性并尝试一些东西。

    MeshPhysicalMaterial 网格物理材质

    MeshPhysicalMaterial与MeshStandardMaterial相同但支持透明涂层效果。您可以控制透明涂层的属性,甚至可以像Three.js 示例中那样使用纹理,但我们不会在这里尝试这个。

    PointsMaterial 点材质

    您可以将PointsMaterial与粒子一起使用。我们将在专门的课程中看到更多相关信息。

    ShaderMaterial 和 RawShaderMaterial

    ShaderMaterialRawShaderMaterial都可以用来创建您自己的材质,但我们将在专门的课程中看到更多相关信息。

    MeshStandardMaterial 材质镜面反射出环境图

    环境贴图就像场景周围的图像。您可以使用它为对象添加反射或折射。它还可以用作照明信息。
    我们还没有介绍它,但您可以将它与我们看到的许多材料一起使用。
    首先,让我们像之前一样使用调试 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5


    要将环境贴图添加到我们的材质中,我们必须使用该envMap属性。Three.js 只支持立方体环境贴图。立方体环境贴图是 6 张图像,每张图像对应环境的一侧。
    您可以在文件夹中找到多个环境贴图/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'
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    您现在可以在材料的envMap属性中使用environmentMapTexture

    material.envMap = environmentMapTexture
    
    • 1


    您应该看到周围环境出现在几何体的表面上。尝试调整metalnessroughness以获得不同的结果。
    您还可以测试文件夹中的其他环境贴图/static/textures/environmentMap/

    环境贴图资源查找

    要找到很酷的环境地图,您可以随时在网络上进行简单的搜索,并确保您有权使用环境地图(如果不是供个人使用)。
    最好的来源之一是HDRIHaven。这个网站有数百个很棒的 HDRI。HDRI 代表高动态范围成像。它们由一个图像(不是立方体贴图)组成,包含比简单图像更多的数据,从而改善光照信息以获得更真实的结果。HDRIHaven图像是免费的,并在CC0 许可下,这意味着您可以对它们做任何您想做的事,而无需注明作者姓名。但如果您欣赏他们的工作,您可以通过订阅他们的 Patreon来感谢他们。
    但是我们有一个问题。正如我们所说,Three.js 仅支持立方体贴图。要将 HDRI 转换为立方体贴图,您可以使用此在线工具:https://matheowis.github.io/HDRI-to-CubeMap/
    上传 HDRI,随意旋转,然后下载由 6 张图像组成的立方体贴图版本。默认格式为.jpg,如果需要,您必须将它们转换为.png

  • 相关阅读:
    C++ 类的继承特性简单运用
    【Python】网络编程
    嵌入式Linux驱动开发 02:将驱动程序添加到内核中
    Vue——方法与事件、 样式绑定、数据绑定、面试题、笔试题
    以一道面试题来探讨测试用例设计的六大思路
    随机过程理论知识(七)
    Node学习七 —— 创建和控制外部进程
    DAC的简单应用
    DW学生美食网页设计作业——餐饮美食汉堡企业网站6页面带轮播(HTML+CSS+JavaScript)
    【Linux】——select详解
  • 原文地址:https://blog.csdn.net/m0_68324632/article/details/131151964