[Three.js-05] Materials

材质(Material)是用来定义物体外观的重要组成部分。它们决定了物体如何反射光线并显示在屏幕上。Three.js 提供了多种不同类型的材质,每种材质都有不同的特性和效果。材质就像物体的皮肤,它决定了一个几何体表面看起来是金属风格、透明、还是线框等等的。这些材质都有一些共有的属性,先介绍这些属性,再对每一种材质进行深入地梳理。

Material 的公共属性

基本属性

材质都继承自一个基类 Material,这个类包含了很多材质共有的属性和方法。

  • id : number

每一个 material 实例都有唯一的编号 idid 的起始值为 0 ,后续每创建同一个类型的材质,id 就会自增 1。

  • name : string

给材质命名的属性。

  • transparent : boolean

表示该材质是否支持透明度设置,开启之后这会对渲染产生影响,因为透明对象需要特殊处理,并且在不透明对象之后进行渲染。

  • opacity: number

表示材质的透明度,取值在 0 到 1 之间,这个属性只有在 transparent 属性设置后才生效。默认值为 1。

  • visible : boolean

表示该材质是否可见。默认值是 true

  • side : Side

定义材质渲染哪一面,可取的值有 THREE.FrontSide :渲染正面、 THREE.BackSide :渲染背面、 THREE.DoubleSide :渲染双面。默认值是 THREE.FrontSide

  • needsUpdate : boolean

用于标记材质是否需要在下一次渲染帧中进行更新。修改了材质的属性时,例如修改了材质的颜色、纹理、透明度等,Three.js 不会立即应用这些更改,而是会将 needsUpdate 属性设置为 true,以标记该材质需要进行更新。WebGL 渲染器渲染场景时,如果物体的材质发生了变化,例如修改了材质的纹理或颜色,那么在渲染下一帧时,WebGL 渲染器会自动检查材质的 needsUpdate 属性,并根据其值来更新材质的内容。

  • colorWrite : boolean

是否渲染材质的颜色。默认值为 true 。如果设置为 false ,则不会显示此材质的颜色(实际上,这样将创建的对象是一个不可见的对象,这些对象会遮挡其后面的对象)。

  • premultipliedAlpha : boolean

用于控制材质的 alpha 通道是否预乘。alpha 通道是图像或纹理中表示透明度的通道,预乘 alpha 是一种在渲染中处理透明度的技术。默认情况下,premultipliedAlpha 属性是设置为 false 的,表示材质的 alpha 通道没有被预乘。如果使用带有预乘 alpha 的纹理(例如 PNG 图像),并且希望在渲染时使用预乘 alpha,可以将 premultipliedAlpha 属性设置为 true。有关预乘详细见下方拓展 预乘

  • dithering : boolean

它用于控制在渲染过程中是否应用抖动(dithering)效果。启用抖动可能会在一些情况下产生额外的计算开销,因为需要在渲染时应用抖动算法。因此,建议仅在需要平滑颜色过渡且颜色深度较低的情况下使用 dithering 属性。如果你的场景使用较高的颜色深度(例如 32 位颜色缓冲区),通常不需要启用抖动。

  • shadowSide: Side | null

side 相似,决定了面材质的的哪一面会产生阴影。如果没有设置,则会遵循 side 属性上设置的值。

  • vertexColors : boolean

开启该属性可以定义要应用于每个顶点的颜色。如果设置为 true,则在渲染中使用顶点上的设置的颜色,反之则不使用顶点的颜色。

混合属性

three.js examples

混合(Blending)是指将场景中的不同像素的颜色值按照一定规则进行组合,从而产生新的颜色效果。混合常用于处理透明度(alpha)和实现各种视觉效果,如半透明、发光、颜色混合等。

为了更好地理解混合过程,以下是一些相关的概念的介绍:

  1. 混合因子(Blending Factors):混合因子是用于控制混合过程中源像素和目标像素的颜色权重的系数。在 Three.js 中,混合因子通过 blendSrcblendDst 属性来设置。blendSrc 表示源像素的混合因子,blendDst 表示目标像素的混合因子。不同的混合因子会产生不同的混合效果。
  2. 源像素(Source Pixel):在混合过程中,源像素是指要进行混合的像素,通常是当前正在渲染的像素。对于 Three.js 中的材质(Material),源像素指的是当前材质的像素颜色。
  3. 目标像素(Destination Pixel):在混合过程中,目标像素是指已经存在于渲染目标(例如帧缓冲区)中的像素。它是源像素要混合到的像素,通常是已经渲染在场景中的像素颜色。
  4. 混合方程式(Blending Equation):混合方程式定义了源像素和目标像素如何进行混合。在 Three.js 中,混合方程式可以通过 blendEquation 属性来设置。默认情况下,混合方程式是标准的加法混合。

在混合过程中,源像素的颜色值根据混合因子进行加权,然后与目标像素的颜色值按照一定规则混合,最终产生混合后的颜色效果。混合过程可以用来实现透明效果、发光效果、颜色混合等,为 Three.js 场景的渲染增添了灵活性和创造性。

Material 所涉及的与混合相关的属性如下:

  • blending: Blending

属性用于控制材质的混合(blending)模式。它决定了材质如何与场景中的其他像素进行混合,从而影响材质的渲染效果。

这个属性的值是一个枚举类型,可以设置为以下几种模式之一:

  • THREE.NoBlending(默认):不进行混合,材质完全覆盖之前的像素。即使材质的透明度(alpha)小于 1,也不会产生透明效果。

  • THREE.NormalBlending:普通混合模式,使用标准的 alpha 混合。材质的透明度小于 1 的像素将进行混合,产生半透明效果。

  • THREE.AdditiveBlending:加法混合模式,将像素的颜色值相加。适用于实现发光或增亮效果。

  • THREE.SubtractiveBlending:减法混合模式,将像素的颜色值相减。用于实现颜色相减效果。

  • THREE.MultiplyBlending:乘法混合模式,将像素的颜色值相乘。用于实现颜色混合效果。

  • THREE.CustomBlending:自定义混合模式,允许使用自定义的混合方程式。这时,你可以通过设置 material.blendSrcmaterial.blendDst 来定义源像素和目标像素的混合因子,以及通过设置 material.blendEquation 来定义混合方程式。

  • blendSrc: BlendingSrcFactor | BlendingDstFactor

用于定义混合(blending)过程中源像素的混合因子。混合因子决定了源像素的颜色在混合过程中的权重,从而影响最终混合后的像素颜色。

  • blendDst: BlendingDstFactor

用于定义混合(blending)过程中目标像素的混合因子。

  • blendSrcAlpha: number | null

blendSrc 的透明度。默认值为 null。

  • blendDstAlpha: number | null

blendDst 的透明度。默认值为 null。

  • blendEquation: BlendingEquation

用于设置混合(blending)过程中混合方程式的类型。混合方程式决定了源像素和目标像素如何进行混合操作,从而影响最终混合后的像素颜色。

高级属性

  • depthTest: true

用于控制是否开启深度测试(Depth Test)。深度测试是一种用于确定在渲染过程中哪些像素应该被绘制的技术。深度测试基于深度缓冲(Depth Buffer)的概念。

depthTest: true(默认值):开启深度测试。物体将按照它们的距离相对于相机的远近进行绘制。较远的物体将被遮挡在较近的物体后面。

depthTest: false:关闭深度测试。不考虑物体的距离,后绘制的物体将覆盖之前绘制的物体,不管它们实际上的位置。

  • depthWrite: true

用于控制是否允许写入深度缓冲(Depth Buffer)。

depthWrite: true(默认值):允许写入深度缓冲。物体的深度值将写入深度缓冲,影响其他物体的深度测试。

depthWrite: false:禁止写入深度缓冲。即使物体在渲染时会参与深度测试,但它的深度值不会被写入深度缓冲。这样,它不会影响其他物体的深度测试,可以在渲染透明对象时使用。

  • depthFunc: DepthModes

用于设置深度测试函数(Depth Test Function)。默认值是 THREE.LessEqualDepth 通常不会修改此属性。

  • alphaTest: number

用于控制透明度测试(Alpha Test)。透明度测试是一种用于确定哪些像素应该被丢弃(不绘制)的技术,通常用于处理半透明纹理的渲染。alphaTest属性定义了一个阈值(threshold),表示当像素的透明度(alpha值)小于或等于该阈值时,该像素将被丢弃。只有透明度大于阈值的像素才会被绘制。阈值通常在0到1之间取值。

  • polygonOffset / polygonOffsetFactor / polygonOffsetUnits

多边形偏移是一种用于解决深度冲突)问题的渲染技术。 多边形偏移通过微调物体的深度值来避免深度冲突问题。具体来说,它会为每个像素的深度值加上一个偏移量,使得物体的深度值略微偏离深度缓冲中已有的值,从而让它们在深度缓冲中稍微不同,避免了深度冲突。

这组属性就是用于解决深度冲突的,开启多边形偏移(Polygon Offset)。

  • polygonOffset:控制是否开启多边形偏移。默认值为false,表示不开启多边形偏移。若设置为true,表示开启多边形偏移。

  • polygonOffsetFactor:多边形偏移的因子。这是一个浮点数值,表示在偏移计算中的比例因子。默认值为0。

  • polygonOffsetUnits:多边形偏移的单位。这是一个浮点数值,表示偏移的单位值。默认值为0。

材质介绍

所有介绍到的 Materail 中关于纹理的属性,将会在后续纹理的文章中介绍。

MeshBasicMaterial

MeshBasicMaterial 是一种简单的材质,常常用于创建简单的几何体。这种材质不受光照的影响,阴影也无法被投射到此种材质的表面。MeshBasicMaterial 是适用于简单渲染需求和性能要求较低的场景,它提供了一种基本的、没有复杂特效的渲染方式。

// 构造函数的参数定义
export interface MeshBasicMaterialParameters extends MaterialParameters {
    color?: ColorRepresentation | undefined;
    opacity?: number | undefined;
    map?: Texture | null | undefined; // 纹理贴图
    lightMap?: Texture | null; // 用于设置光照贴图
    lightMapIntensity?: number | undefined; // 光照贴图强度
    aoMap?: Texture | null | undefined; // 用于设置环境遮挡贴图
    aoMapIntensity?: number | undefined; // 环境遮挡贴图强度
    specularMap?: Texture | null | undefined; // 设置镜面高光贴图
    alphaMap?: Texture | null | undefined; // 用于设置透明度贴图
    fog?: boolean | undefined; // 设置材质是否被 fog 影响
    envMap?: Texture | null | undefined; // 用于设置环境贴图
    combine?: Combine | undefined; // 用于控制如何将材质的颜色和纹理与场景中的光照相结合。
    reflectivity?: number | undefined; // 用于控制具有环境贴图(envMap)的材质在反射环境光照时的强度
    refractionRatio?: number | undefined;// 用于控制材质表面的折射效果。
    wireframe?: boolean | undefined; // 是否以线框模式渲染
    wireframeLinewidth?: number | undefined;  
    wireframeLinecap?: string | undefined; 
    wireframeLinejoin?: string | undefined;
}

const props:MeshBasicMaterialParameters = {
    //...
}

const material = new THREE.MeshBasicMaterial(props)

MeshDepthMaterial

MeshDepthMaterial 用于在渲染场景时根据物体与相机之间的距离来绘制深度信息,深度是基于相机远近平面来计算的,距离近截面近的颜色越白,距离近平面越远颜色越来越黑。这种材质不会考虑光照和阴影效果。

[Three.js-05] Materials

// 构造函数的参数定义
export interface MeshDepthMaterialParameters extends MaterialParameters {
    map?: Texture | null | undefined;
    alphaMap?: Texture | null | undefined;
    depthPacking?: DepthPackingStrategies | undefined;
    displacementMap?: Texture | null | undefined; // 用于实现几何形状的位移效果的位移贴图
    displacementScale?: number | undefined;// 用于控制位移贴图对几何体的顶点产生的位移强度
    displacementBias?: number | undefined; // 用于控制位移贴图的偏移量
    wireframe?: boolean | undefined;
    wireframeLinewidth?: number | undefined;
}

const depthMaterial = new THREE.MeshDepthMaterial();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, depthMaterial);
scene.add(mesh);

这种材质通常配合其他材质一起使用实现景深效果,另外的材质必须设置 blending 属性。

[Three.js-05] Materials

const geo = new THREE.BoxGeometry(5, 5, 5)
const depthMaterial = new THREE.MeshDepthMaterial();
const otherMaterial = new THREE.MeshBasicMaterial({
    color: 'hotpink',
    transparent: true, 
    blending: THREE.AdditiveBlending,
})
cube = createMultiMaterialObject(geo, [depthMaterial, otherMaterial]) 
scene.add(cube)

MeshNormalMaterial

MeshNormalMaterial 是一种特殊类型的材质(Material),用于显示模型的法线信息。它会根据模型的法线方向来渲染模型的表面颜色,从而可视化法线的方向和强度。

法线是垂直于模型表面的矢量,它在计算机图形学中常用于实现光照、阴影和表面细节等效果。MeshNormalMaterial 可以直观地看到模型表面的法线方向,对调试和优化模型的法线数据非常有用。

export interface MeshNormalMaterialParameters extends MaterialParameters {
    bumpMap?: Texture | null | undefined; // 设置凹凸效果,凹凸贴图
    bumpScale?: number | undefined; // 用于控制凹凸贴图的强度
    normalMap?: Texture | null | undefined; // 用于实现法线贴图效果
    normalMapType?: NormalMapTypes | undefined; // 用于指定法线贴图的类型。
    normalScale?: Vector2 | undefined; // 用于调整法线贴图的强度
    displacementMap?: Texture | null | undefined;
    displacementScale?: number | undefined;
    displacementBias?: number | undefined;
    wireframe?: boolean | undefined;
    wireframeLinewidth?: number | undefined;

    flatShading?: boolean | undefined;
}

[Three.js-05] Materials

const normalMaterial = new THREE.MeshNormalMaterial()
const box = new THREE.BoxGeomatry(5, 5,5)
const mesh = new THREE.Mesh(box, normalMaterial)

MeshNormalMaterial 有一个属性 flatShading ,用于控制模型的着色方式。默认情况下,flatShading 的值为 falseflatShadingfalse 表示使用光滑着色(Smooth Shading),反之则使用平面着色。平面找着色就是按照几何体的顶点原封原样的渲染,而光滑着色就是在几何体顶点之间采取了平滑的过度渲染,这样物体的表面更光滑。

[Three.js-05] Materials

MeshLambertMaterial

MeshLambertMaterial 是一种基础的材质,它是基于 Lambert 反射模型的材质,实现了一种简单的漫反射光照效果。

Lambert 反射模型是一种经典的光照模型,它假设表面对于所有方向的入射光线都是均匀地反射,即无论观察者位于表面的什么位置,表面都会均匀地反射光线,看到的表面颜色都是一致的。因此,MeshLambertMaterial 不考虑光线的方向,只计算漫反射光照,不会产生高光(镜面反射)效果。

const material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshLambertMaterial 的常用属性

  • emissive 用于设置模型的自发光颜色。自发光它使模型表面产生一种类似于发光材质的效果,实际上物体没有发光,只是通过表面颜色模拟了发光的效果。emissive 属性接受一个颜色值,表示模型的自发光颜色。默认情况下,emissive 的值为黑色,即模型没有自发光。
  • emissiveIntensity 用于调整模型的自发光强度。 这是一个数字属性,表示模型的自发光强度倍数。

[Three.js-05] Materials

const material = new THREE.MeshLambertMaterial();
const geometry = new THREE.BoxGeometry(1, 1, 1);

material.color = new THREE.Color(0xa0b314)
material.emissive = new THREE.Color(0x042f27)
material.emissiveIntensity = 0.5

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshLambertMaterial 是一种基础的材质,适用于简单的渲染需求,可以用于以下场景:

  1. 漫反射表面: MeshLambertMaterial 主要用于渲染漫反射表面,例如木材、石头、布料等材质,这些材质通常不会产生高光反射效果。

  2. 低光泽表面: 对于没有明显光泽的表面,MeshLambertMaterial 可以产生逼真的渲染效果。光泽度越低的表面越适合使用 Lambert 反射模型。

  3. 没有明确光源的场景: MeshLambertMaterial 不需要明确的光源来计算高光反射,适用于一些不需要高级光照效果的简单场景,例如一些 3D 游戏中的低多边形模型。

MeshPhongMaterial

MeshPhongMaterial 是基于 Phong 反射模型提供高光反射效果的材质。

const material = new THREE.MeshPhongMaterial({
  color: 0xff0000,
  specular: 0xffffff, // 高光颜色
  shininess: 50, // 高光亮度
});

const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshPhongMaterial 常用属性:

  • emissiveMeshLambertMaterial
  • emissiveIntensityMeshLambertMaterial
  • specular 用于设置模型的高光颜色。高光是指在表面上由于光线的入射角和视线方向之间的夹角产生的明亮反射现象。高光通常出现在具有光泽的材质表面,如金属、塑料和湿润的表面等。通过调整高光颜色,可以改变高光的外观,从而影响模型的视觉效果。
  • shininess 用于控制模型的高光反射亮度。数值越高,高光越小而越锐利,数值越低,高光越大而越模糊。

[Three.js-05] Materials

MeshPhongMaterial 主要用于以下场景:

  1. 具有光泽和高光反射的材质: MeshPhongMaterial 提供了高光反射效果,适用于渲染具有光泽和高光反射的材质,如金属、塑料、陶瓷等。

  2. 真实光照效果的场景: Phong 反射模型相对于 Lambert 反射模型提供了更真实的光照效果,对于需要更加真实的光照渲染的场景,可以选择使用 MeshPhongMaterial

  3. 需要明显高光反射的表面: 对于一些具有明显高光反射效果的表面,例如宝石、玻璃等,MeshPhongMaterial 可以提供逼真的高光渲染效果。

MeshToonMaterial

MeshToonMaterial 用于实现卡通风格的渲染效果的材质。

[Three.js-05] Materials

基本使用方式:

const material = new THREE.MeshToonMaterial({ color: 0xff0000 });
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshToonMaterial 也支持 emissive 相关 属性 和 wireframe

MeshStandardMaterial

MeshStandardMaterial 是 一种高级的 PBR(Physically Based Rendering)材质,用于实现基于物理的渲染效果,提供更加真实的光照和材质反射模拟。PBR 材质模型基于真实世界中的光学物理原理,可以模拟光线的传播、反射、折射等现象,以获得逼真的渲染效果。

[Three.js-05] Materials

const material = new THREE.MeshStandardMaterial({
  color: 0x777777,  
  roughness: 0.5, // 粗糙度为 0.5
  metalness: 0, // 金属度为 0
});

const geometry = new THREE.SphereGeometry(10);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshStandardMaterial 重要属性:

  • metalness 属性控制材质的金属程度。数值越接近 1,表明材质更接近金属性质,数值越接近 0,表明材质更接近非金属性质。金属材质的特点是能够反射大部分光线,并且具有明显的高光反射。非金属材质通常会散射大部分光线,并且不会产生明亮的高光反射。
  • roughness 属性控制表面的粗糙度程度。一个完全光滑的表面(roughness 值为 0)将产生清晰和锐利的反射高光;而一个非常粗糙的表面(roughness 值接近 1)将产生模糊和扩散的反射,类似于毛玻璃或磨砂材质。

MeshStandardMaterial 适用于需要较为真实光照和材质反射的场景,例如渲染金属、玻璃、塑料等材质,以及需要使用环境贴图来模拟反射环境的场景。

MeshPhysicalMaterial

MeshPhysicalMaterial 用于实现更加真实的光照和材质反射效果。与 MeshStandardMaterial 类似,MeshPhysicalMaterial 也属于 PBR 材质模型,但在某些方面提供了更多的控制选项,使得渲染效果更加精细和逼真。

相比MeshStandardMaterial增加了两个属性:clearCoatclearCoatRoughness 用于控制清漆效果的。清漆(Clear Coat)效果是一种光照模拟,用于模拟在物体表面涂有一层光滑的透明涂层,使物体看起来更加光滑和亮丽。

  • clearCoat 属性用于控制清漆层的强度,它是一个介于 0 到 1 之间的值。数值越高,清漆层越强烈,数值越低,清漆层越弱或没有效果。清漆效果在光照下产生高光反射,增加了物体的反射亮度和视觉效果。通常用于模拟高光的增强和反射面的增加,使物体看起来更加光滑和明亮。
  • clearCoatRoughness属性用于控制清漆层的粗糙度,它是一个介于 0 到 1 之间的值。数值越高,清漆层越模糊,数值越低,清漆层越光滑。清漆层粗糙度影响高光的大小和锐利度。当 clearCoat 属性大于 0 时,clearCoatRoughness 越接近 0,清漆层的高光越小而越锐利,越接近 1,清漆层的高光越大而越模糊。

[Three.js-05] Materials

const material = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  roughness: 0.5,
  metalness: 0.8,
  clearcoat: 0.5, // 清漆层强度为 0.5
  clearcoatRoughness: 0.3, // 清漆层粗糙度为 0.3
});

const geometry = new THREE.SphereGeometry(10);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MeshPhysicalMaterial 可以更加细致地控制材质的外观,包括粗糙度、金属度、反射率、清漆效果和透明度等。它适用于需要更高级的渲染效果、更真实的光照和材质反射模拟的场景。

ShadowMaterial

ShadowMaterial 用于在阴影贴图中渲染对象。它主要用于生成阴影贴图,而不是在场景中呈现实际的可见对象。这个材质可以用来创建自定义阴影效果,例如将阴影显示为特定的颜色或添加其他效果。只会显示物体表面的阴影部分。

[Three.js-05] Materials

const shadowMaterial = new THREE.ShadowMaterial({ color: 0x777777 })
const fox = await sampleFox(shadowMaterial)
scene.add(fox)

该材质主要用来自定义阴影颜色或效果,使用 ShadowMaterial 可以自定义阴影的颜色、透明度或其他效果,从而在阴影贴图中呈现特定的视觉效果。可以将阴影显示为不同的颜色,使阴影在场景中更加显眼,或者使用纹理贴图来增加阴影的细节。

LineBasicMaterial

LineBasicMaterial 用于渲染线段的基本材质 。它用于创建简单的线段对象,并可以设置线段的颜色、透明度、线宽等属性。

[Three.js-05] Materials

  • linewidth 线段的宽度
  • vertexColors 使用顶点色
const points = [];
points.push(new THREE.Vector3(-30, 0, 0));
points.push(new THREE.Vector3(0, 60, 0));
points.push(new THREE.Vector3(10, 20, 10));

const geometry = new THREE.BufferGeometry().setFromPoints(points);

const colors = new Float32Array(geometry.attributes.position.count * 3);
// 为每个顶点设置不同的颜色,这里使用随机颜色作为示例
for (let i = 0; i < colors.length; i += 3) {
    colors[i] = Math.random(); // 红色分量
    colors[i + 1] = Math.random(); // 绿色分量
    colors[i + 2] = Math.random(); // 蓝色分量
}
const colorAttribute = new THREE.BufferAttribute(colors, 3);
geometry.setAttribute('color', colorAttribute);

const line = new THREE.Line(geometry, material);
line.castShadow = true
line.receiveShadow = true
scene.add(line);

LineDashedMaterial

LineDashedMaterial 继承自 LineBasicMaterial,可以在基本线段的基础上添加虚线效果,用于创建带有虚线样式的线段,用于表示边界、轮廓、路径等。它通常用于可视化场景中的辅助线或标记线,使其在渲染时具有更醒目和特殊的视觉效果。

  • scale:虚线的缩放比例,用于调整虚线的密度和间隔。
  • dashSize:虚线中每个短线的长度。
  • gapSize:虚线中每个间隔的长度。

[Three.js-05] Materials

const sampleGosper = (material) => {
const points = gosper(4, 50)
const colors = new Float32Array(points.length * 3)
const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
points.forEach((e, i) => {
const color = new THREE.Color(0xffffff)
color.setHSL(e.x / 100 + 0.5, (e.y * 20) / 400, 0.2)
colors[i * 3] = color.r
colors[i * 3 + 1] = color.g
colors[i * 3 + 2] = color.b
})
lineGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true))
const mesh = new THREE.Line(lineGeometry, material)
mesh.computeLineDistances()
mesh.scale.set(0.1, 0.1, 0.1)
mesh.translateY(-2)
return mesh
}
function gosper(a, b) {
var turtle = [0, 0, 0]
var points = []
var count = 0
rg(a, b, turtle)
return points
function rt(x) {
turtle[2] += x
}
function lt(x) {
turtle[2] -= x
}
function fd(dist) {
points.push({
x: turtle[0],
y: turtle[1],
z: Math.sin(count) * 5
})
var dir = turtle[2] * (Math.PI / 180)
turtle[0] += Math.cos(dir) * dist
turtle[1] += Math.sin(dir) * dist
points.push({
x: turtle[0],
y: turtle[1],
z: Math.sin(count) * 5
})
}
function rg(st, ln, turtle) {
st--
ln = ln / 2.6457
if (st > 0) {
rg(st, ln, turtle)
rt(60)
gl(st, ln, turtle)
rt(120)
gl(st, ln, turtle)
lt(60)
rg(st, ln, turtle)
lt(120)
rg(st, ln, turtle)
rg(st, ln, turtle)
lt(60)
gl(st, ln, turtle)
rt(60)
}
if (st == 0) {
fd(ln)
rt(60)
fd(ln)
rt(120)
fd(ln)
lt(60)
fd(ln)
lt(120)
fd(ln)
fd(ln)
lt(60)
fd(ln)
rt(60)
}
}
function gl(st, ln, turtle) {
st--
ln = ln / 2.6457
if (st > 0) {
lt(60)
rg(st, ln, turtle)
rt(60)
gl(st, ln, turtle)
gl(st, ln, turtle)
rt(120)
gl(st, ln, turtle)
rt(60)
rg(st, ln, turtle)
lt(120)
rg(st, ln, turtle)
lt(60)
gl(st, ln, turtle)
}
if (st == 0) {
lt(60)
fd(ln)
rt(60)
fd(ln)
fd(ln)
rt(120)
fd(ln)
rt(60)
fd(ln)
lt(120)
fd(ln)
lt(60)
fd(ln)
}
}
}
const gosperMesh = sampleGosper(material)
gosperMesh.castShadow = true
gosperMesh.receiveShadow = true
scene.add(gosperMesh);

ShaderMaterial

ShaderMaterial 是一种高级材质,它允许使用自定义的顶点着色器和片元着色器来实现对物体的渲染效果控制。相比于内置的材质类型,ShaderMaterial 提供了更高的灵活性和自定义能力,可以实现各种复杂的渲染效果,包括纹理混合、特殊效果、光照模拟、动画等。

要创建 ShaderMaterial,需要编写自定义的 GLSL 着色器代码,包括顶点着色器和片元着色器。顶点着色器主要用于处理顶点数据的变换和传递,片元着色器用于计算每个像素的颜色值。

以下是创建 ShaderMaterial 的基本步骤:

  1. 编写顶点着色器和片元着色器代码。
  2. 创建一个 THREE.ShaderMaterial 对象,并将顶点着色器和片元着色器代码传入材质。
  3. 将外部的 uniform 变量和顶点属性传递给着色器,用于控制渲染效果。
  4. 创建一个几何体(THREE.GeometryTHREE.BufferGeometry),并将材质应用到几何体的 Mesh 上。

[Three.js-05] Materials

const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// uniform vec3 color; 接收js代码中传递的 color
const fragmentShader = `
varying vec2 vUv;
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
`;
const material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: {
// 传递一个颜色参数给着色器
color: { value: new THREE.Vector3(1, 0, 0) }, 
},
});
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

其他

组合使用多种材质

createMultiMaterialObject 用于将多种材质应用到一个几何体上,并创建一个包含多个子网格的对象。每个子网格使用不同的材质进行渲染,从而实现一个几何体使用不同材质的效果。

参数说明:

  • geometry:表示要应用材质的几何体。可以是 THREE.GeometryTHREE.BufferGeometry 类型的几何体对象。
  • materials:表示一个包含多种材质的数组。每个元素都是一个 THREE.Material 类型的材质对象,用于对应几何体的不同部分。

函数返回一个 THREE.Object3D 对象,该对象包含多个子网格,每个子网格都使用对应的材质进行渲染。

最后,我们将这个包含多个材质的对象添加到场景中。

import { createMultiMaterialObject } from 'three/examples/jsm/utils/SceneUtils'
const mat1 = new THREE.MeshBasicMaterail()
const mat2 = new THREE.MeshBasicMaterail()
const mat3 = new THREE.MeshBasicMaterail()
// ...
const matList = [mat1, mat2, mat3,  ... ]
const geo = new THREE.BoxGeometry(10,10,10)
const box = createMultiMaterialObject(geo, matList)
// 修改属性必须 遍历所有子节点一个一个修改
box.children.forEach((item)=>{
item.castShadow = true
})
scene.add(box)

为同一个Mesh的不同面使用不同的材质

const mat1 = new THREE.MeshBasicMaterial({ color: 0x777777 })
const mat2 = new THREE.MeshBasicMaterial({  color: 0xff0000 })
const mat3 = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const mat4 = new THREE.MeshBasicMaterial({ color: 0x0000ff })
const mat5 = new THREE.MeshBasicMaterial({ color: 0x66aaff })
const mat6 = new THREE.MeshBasicMaterial({ color: 0xffaa66 })
const matArray = [mat1, mat2, mat3, mat4, mat5, mat6]
const cubeGeom = new THREE.BoxGeometry(1, 1, 1, 10, 10, 10)
const cubeMesh = new THREE.Mesh(cubeGeom, material)

这样就为立方体的六个面上应用了不同的材质。

扩展

预乘 alpha

透明度(alpha)通道: 在计算机图形学中,每个像素通常由红(R)、绿(G)、蓝(B)三个颜色通道组成,分别表示像素的红色、绿色和蓝色分量。此外,还有一个称为透明度通道(alpha)的额外通道,用于表示像素的透明度或不透明度。alpha 通道的取值范围通常是从完全不透明(1)到完全透明(0)。

颜色混合: 在渲染图像时,特别是涉及到半透明物体或图像叠加时,需要将不同像素的颜色进行混合。这就涉及到颜色混合的计算。简单的颜色混合是将两个颜色值按照一定比例相加,这种混合方式称为线性混合。

在预乘 alpha 技术中,将 alpha 值乘以 RGB 分量,得到预乘后的 RGB 分量。这样做的好处是,在进行颜色混合或透明度叠加等操作时,可以更高效地处理透明像素的渲染。预乘 alpha 可以避免由于 alpha 混合而产生的不透明效果,并减少图像渲染中的伽马校正计算。

下面是预乘 alpha 的计算方式:

预乘后的红色分量 (R’) = 原始红色分量 (R) × alpha 预乘后的绿色分量 (G’) = 原始绿色分量 (G) × alpha 预乘后的蓝色分量 (B’) = 原始蓝色分量 (B) × alpha

在预乘 alpha 的颜色值中,RGB 分量的取值范围通常是 [0, alpha]。当 alpha 为 1(完全不透明)时,预乘后的颜色值与原始颜色值相同。当 alpha 为 0(完全透明)时,预乘后的颜色值为 (0, 0, 0)(全黑)。

预乘 alpha 在处理半透明物体、图像合成、特效等场景中经常被使用。它可以提高渲染效果和性能,尤其在 GPU 硬件渲染中更加高效。在实际应用中,你可以根据具体场景和需求来决定是否使用预乘 alpha 来优化渲染效果和性能。

抖动

图像处理之 Dithering

抖动(Dithering)是一种在图像渲染和颜色深度较低的情况下用于减少颜色带来的视觉不连续性的技术。在低颜色深度环境下,例如使用 16 位颜色缓冲区(16-bit color buffer),渐变或细节可能会出现明显的带状效果。抖动通过在颜色上应用空间或时间上的噪声来模拟更平滑的渐变和细节,从而改善图像的质量和视觉效果。

工作原理: 当颜色深度有限时,每个像素只能表示有限数量的颜色。例如,对于一个 16 位颜色缓冲区,每个 R、G 和 B 分量只能使用 5 位表示,因此只有 32 种不同的红色、32 种不同的绿色和 32 种不同的蓝色。这限制了渲染过程中能够显示的颜色数量,可能导致颜色的平滑过渡变得不够流畅。

抖动通过在渲染像素时,对像素的颜色进行微小的变化,以逼近原本无法表示的颜色。这些微小的颜色变化被引入的噪声称为“抖动”。当相邻像素使用稍微不同的颜色时,它们混合在一起后会在视觉上形成更平滑的过渡效果,从而减少了色带和色块的出现。

抖动技术通常有两种类型:

  1. 空间抖动:在空间抖动中,对于每个像素,根据其在图像中的位置应用随机的抖动。这样可以在整个图像中分布抖动,从而减少色带效应。
  2. 时间抖动:在时间抖动中,使用一个预先定义的抖动序列来为连续帧应用不同的颜色。通过在连续帧之间进行颜色变化,可以模拟更多的颜色,从而提高图像质量。

抖动技术在低颜色深度环境中是一种弥补可视化不足的有效方法。然而,对于高颜色深度的现代图像渲染环境,抖动往往不再是必需的,因为有足够的颜色空间可以展示平滑的过渡效果。因此,在使用抖动时需要权衡图像质量和计算开销。

总结: 抖动是一种在低颜色深度环境下用于改善图像质量的技术。它通过引入随机噪声,模拟更多的颜色,减少颜色带和色块的出现,从而实现更平滑的颜色过渡和更高质量的视觉效果。在现代图像渲染环境中,通常只在颜色深度较低的场景或特殊需求下使用抖动技术。

深度测试

深度测试用于解决物体的可见性和遮挡关系,以确保场景中的物体按照正确的顺序绘制在屏幕上。深度测试主要基于深度缓冲(Depth Buffer)的概念,是实现真实感三维渲染的重要组成部分。

深度缓冲是一种像素级的缓冲区,用于存储场景中每个像素的深度值。深度值表示物体离相机的距离,通常以相机视点到物体的距离作为深度值。深度缓冲初始化时被设置为最远的值(通常是1.0或者更大),当场景渲染时,深度测试会根据新的深度值与深度缓冲中的值进行比较,然后决定是否将像素绘制到屏幕上。

深度测试的过程如下:

  1. 当场景中的一个像素要被渲染时,计算该像素对应的深度值。
  2. 比较该像素的深度值与深度缓冲中相应位置的值。
  3. 如果该像素的深度值小于深度缓冲中的值,表示该像素位于前面(更接近相机),则将该像素绘制到屏幕上,并更新深度缓冲中的值为该像素的深度值。
  4. 如果该像素的深度值大于等于深度缓冲中的值,表示该像素位于后面(被其他物体遮挡或更远离相机),则丢弃该像素,不进行绘制。

深度测试的目的是确保物体按照正确的顺序绘制,从而解决遮挡关系,让场景看起来更真实。没有深度测试,渲染器会简单地按照物体被添加到场景中的顺序进行绘制,导致后面的物体可能遮挡前面的物体,破坏了真实感和逼真度。

在Three.js中,默认情况下,深度测试是开启的,并且使用”小于等于”的深度测试函数(THREE.LessEqualDepth)。通常情况下,不需要手动设置深度测试,但在某些场景中,可能会需要关闭深度测试或者使用其他的深度测试函数,特别是在渲染透明物体时。

透明度测试

透明度测试是一种用于处理半透明纹理或材质的技术。在渲染半透明的纹理或物体时,如带有透明背景的纹理、透明的精灵(Sprite)或玻璃等,透明度测试非常有用。

透明度测试的目的是确保只有透明度(alpha值)大于某个设定的阈值时,像素才会被绘制到屏幕上。这样可以避免绘制透明的部分,从而提高渲染效率并得到正确的透明效果。透明度测试通常在深度测试之后执行,确保透明物体正确地遮挡后面的物体。

透明度测试的步骤如下:

  1. 当场景中的一个像素要被渲染时,获取该像素对应材质的透明度(alpha值)。
  2. 比较该透明度与alphaTest属性设置的阈值。
  3. 如果透明度大于阈值,表示该像素是不透明的,将会被绘制到屏幕上。
  4. 如果透明度小于或等于阈值,表示该像素是透明的,将被丢弃(不绘制)。

在Three.js中,alphaTest属性用于控制透明度测试的阈值。可以通过设置材质的alphaTest属性来指定一个阈值(通常是0到1之间的值),决定哪些像素应该被绘制。

深度冲突

深度冲突是一种在计算机图形学中常见的问题。它发生在渲染场景时,当两个或多个物体(通常是平面或接近平面的物体)处于非常接近的位置时,它们的深度值在深度缓冲中可能会非常接近,甚至相等,从而导致在屏幕上产生可见的闪烁、抖动或条纹状问题。

造成深度冲突的主要原因是由于浮点数精度限制和深度缓冲的有限精度。在深度缓冲中,每个像素的深度值用浮点数表示,而浮点数的精度是有限的。当物体非常接近时,它们的深度值可能会落在深度缓冲中相同的位置上,导致渲染器无法区分哪个像素应该在前面、哪个像素应该在后面,从而产生深度冲突问题。

解决深度冲突问题的方法通常有以下几种:

  1. 增加深度缓冲的精度:可以通过增加深度缓冲的位数来提高深度缓冲的精度,减少Z-fighting问题的出现。这可以通过将深度缓冲的位数从16位增加到24位或32位来实现。不过这种方法可能会占用更多的内存和计算资源。

  2. 使用更合适的深度范围:根据场景的需求,调整相机的近裁面和远裁面来适当地调整深度缓冲的范围。较小的深度范围可以提高深度缓冲的精度,减少深度冲突。

  3. 增加几何体的面数:通过增加几何体的面数,使其更加复杂,可以减少深度冲突。但这可能会增加渲染的开销,因为更复杂的几何体需要更多的计算资源。

  4. 使用多边形偏移:多边形偏移是一种在渲染时微调物体的深度值的技术。它通过给物体的深度值加上一个小的偏移量,使得它们在深度缓冲中的值稍微不同,从而避免深度冲突。多边形偏移通常用于解决两个非常接近的物体之间的深度冲突。

  5. 使用不同的渲染顺序:在渲染多个平面或透明物体时,可以通过调整它们的渲染顺序来避免深度冲突。将最远的物体先渲染,再渲染较近的物体,可以减少深度冲突的可能性。

Lambert 反射模型

Lambert 反射模型(Lambertian Reflection Model)是计算机图形学中用于描述表面漫反射光照的一种基本光照模型。它是由瑞士数学家和物理学家 Johann Lambert 在18世纪中叶提出的,并被广泛用于计算机图形学和计算机游戏等领域。

Lambert 反射模型基于以下基本假设:

  1. 光线照射到表面后会均匀地向所有方向进行漫反射,即不会出现高光(镜面反射)现象。这意味着表面在任何方向上的观察者看到的亮度是相同的。
  2. 表面的亮度与光线照射的角度和表面法线之间的夹角有关。夹角越大,表面接收到的光线越少,亮度越暗;夹角越小,表面接收到的光线越多,亮度越亮。

Lambert 反射模型可以用以下公式表示:

I = I₀ * k * cos(θ)

其中:

  • I 表示表面的亮度;
  • I₀ 表示入射光的强度;
  • k 表示表面的反射系数,即表面的漫反射属性;
  • θ 表示入射光线和表面法线之间的夹角。

根据这个公式,当光线垂直于表面法线时(θ = 0),亮度最大,当光线和表面法线垂直时(θ = π/2),亮度为零,即表面完全不可见。

Lambert 反射模型适用于无光泽、粗糙或漫反射表面的渲染,例如石头、木材、瓷砖等材质。它是计算机图形学中最简单的光照模型之一,虽然不考虑高光反射和阴影,但在某些场景下仍能产生逼真的效果。

需要注意的是,Lambert 反射模型是一种近似的模型,对于高光、镜面反射和其他复杂光照现象无法很好地描述。在实际应用中,通常会与其他光照模型或环境光等结合使用,以获得更加真实的渲染效果。

Phong 反射模型

Phong 反射模型是计算机图形学中的一种光照模型,由Bui Tuong Phong在1973年提出,用于描述表面的光照反射。它是对 Lambert 反射模型的改进,引入了高光反射的概念,可以更加真实地模拟光照效果。

Phong 反射模型基于三个主要的光照成分,分别是环境光反射、漫反射和高光反射。下面是每个成分的详细介绍:

  1. 环境光反射(Ambient Reflection): 环境光是指在周围环境中存在的全局光照,不受具体光源的影响。在 Phong 反射模型中,环境光反射通常简单地将模型的表面都均匀地照亮,使得整个场景都有一个基础亮度。
  2. 漫反射(Diffuse Reflection): 漫反射是指光线照射到表面后均匀地向所有方向反射。在 Phong 反射模型中,漫反射成分根据光线入射角和表面法线的夹角来计算,夹角越大,表面接收到的光线越少,亮度越暗;夹角越小,表面接收到的光线越多,亮度越亮。
  3. 高光反射(Specular Reflection): 高光反射是指由于光线的入射角和视线方向之间的夹角产生的明亮反射现象。在 Phong 反射模型中,高光反射成分可以模拟具有光泽表面的物体上产生的亮光。高光反射的亮度和大小取决于表面的光泽程度和视角,光泽越大,高光越小而越锐利。

综合这三个成分,Phong 反射模型的计算公式如下:

I = Ia * Ka + Id * Kd * (L · N) + Is * Ks * (R · V)^n

其中:

  • I 表示表面的最终亮度;
  • Ia 表示环境光的强度;
  • Ka 表示表面的环境光反射系数;
  • Id 表示漫反射光的强度;
  • Kd 表示表面的漫反射反射系数;
  • L 表示光线的方向向量;
  • N 表示表面的法线向量;
  • Is 表示高光反射光的强度;
  • Ks 表示表面的高光反射系数;
  • R 表示反射光线的方向向量;
  • V 表示视线的方向向量;
  • n 表示高光反射的粗糙度指数,通常取值在 1 到 100 之间。

Phong 反射模型是一种简单而实用的光照模型,常用于计算机图形学中的渲染和着色。然而,它也有一些缺点,例如在产生真实光照效果方面仍然存在一定的局限性,而且计算成本较高。在实际应用中,Phong 反射模型常常与其他高级光照模型结合使用,以获得更加真实和逼真的渲染效果。

原文链接:https://juejin.cn/post/7258896295616184378 作者:kg

(0)
上一篇 2023年7月24日 上午10:15
下一篇 2023年7月24日 上午10:26

相关推荐

发表回复

登录后才能评论