| 材质 | 属性 | 含义 | 使用方法 | 范围及效果 |
|---|---|---|---|---|
| MeshStandardMaterial,MeshPhysicalMaterial | metalness | 金属度 | mesh.material.metalness = 1.0; | 0-1,值越大越偏金属 |
| MeshStandardMaterial,MeshPhysicalMaterial | roughness | 粗糙度 | mesh.material.roughness = 0.5; | 0-1,值越小越能光滑反射 |
| MeshPhysicalMaterial | clearcoat | 清漆层 | mesh.material.clearcoat = 1; | 0-1,值越大越清透 |
| MeshPhysicalMaterial | clearcoatRoughness | 清漆层粗糙度 | mesh.material.clearcoatRoughness= 0.01; | 0-1,值越小越清透 |
| MeshPhysicalMaterial | transmission | 透光率 | mesh.material.transmission= 0.5; | 值越大越透光 |
| MeshPhysicalMaterial | ior | 折射率 | mesh.material.ior= 1 | 1-2.3333,值越小越清透 |
官方文档:
物理网格材质(MeshPhysicalMaterial)
标准网格材质(MeshStandardMaterial)
一、简介PBR
PBR就是,基于物理的渲染(physically-based rendering),可以通过光照模型模拟物理表面的光照。
Three.js提供了两个PBR材质相关的APIMeshStandardMaterial和MeshPhysicalMaterial,MeshPhysicalMaterial 是 MeshStandardMaterial 扩展的子类,提供了更多功能属性。
网格模型材质

MeshLambertMaterial: Lambert光照模型(漫反射)
MeshPhongMaterial:Phong光照模型(漫反射、高光反射)
MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理论、能量守恒、菲涅尔反射...)
渲染占用资源和表现能力
PBR材质相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的、更接近生活中的材质效果,当然也会占用更多的电脑硬件资源。
渲染表现能力越强,占用的计算机硬件资源更多。
占用渲染资源 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial
渲染表现能力 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial
二、PBR材质的常用属性
1、金属度metalness
- 含义:材质像金属的程度
- 范围:0.0 - 1.0 (非金属材料,如木材或石材,使用0.0;金属使用1.0)
- 示例:把 metalness 从 0 调到 1 看下效果(从木头疙瘩变成不锈钢了属于是
bash
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
loader.load("../../金属.glb", function (gltf) {
gltf.scene.traverse(function(obj){
if(obj.isMesh){
console.log('obj.material',obj.material);
obj.material.metalness = 0; // *金属度
}
})
model.add(gltf.scene);
})


2、粗糙度roughness
- 含义:物体表面粗糙程度
- 范围:0.0 - 1.0 (
0.0表示平滑的镜面反射;1.0表示完全漫反射;默认0.5) - 示例:把 roughness从 0 调到 1 看下效果(为0的时候像在鼻子上戳了一个高光,0.6的时候是把高光拍散了的感觉
bash
loader.load("../../金属.glb", function (gltf) {
gltf.scene.traverse(function(obj){
if(obj.isMesh){
console.log('obj.material',obj.material);
obj.material.metalness = 0.4; // *金属度
obj.material.roughness = 0; // *粗糙度
}
})
model.add(gltf.scene);
})


三、环境贴图
1、概念
环境贴图就是模拟周围环境的图像。比如一间房子,房子的上下左右前后分别拍摄一张照片,就是3D空间中6个角度方向的照片。
一个物体表面,往往会反射周围的环境。如果想渲染效果更好看,更符合实际生活情况,需要想办法让模型反射周围景物。
2、CubeTextureLoader 立方体纹理加载器
加载环境贴图 CubeTextureLoader
TextureLoader返回TextureCubeTextureLoader返回CubeTexture
通过前面学习大家知道,通过纹理贴图加载器TextureLoader的.load()方法加载一张图片可以返回一个纹理对象Texture。
立方体纹理加载器CubeTextureLoader的.load()方法是加载6张图片,返回一个立方体纹理对象CubeTexture。
立方体纹理对象CubeTexture的父类是纹理对象Texture
环境贴图.envMap(对单个材质)
未加环境图:

加了环境图:

bash
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
const textureCube = new THREE.CubeTextureLoader()
.setPath('../../环境贴图/环境贴图0/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
loader.load("../../金属.glb", function (gltf) {
// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function(obj) {
if (obj.isMesh) {//判断是否是网格模型
// console.log('obj.material',obj.material);
// 重新设置材质的金属度和粗糙度属性
obj.material.metalness = 1.0;//金属度
obj.material.roughness = 0.35;//表面粗糙度
obj.material.envMap = textureCube;
}
});
model.add(gltf.scene);
})
PS: 不同粗糙度roughness也会影响环境贴图对材质反光的效果。
环境贴图反射率.envMapIntensity
默认值1, 设置为0.0,相当于没有环境贴图
bash
loader.load("../../金属.glb", function (gltf) {
// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function(obj) {
if (obj.isMesh) {//判断是否是网格模型
// console.log('obj.material',obj.material);
// 重新设置材质的金属度和粗糙度属性
obj.material.metalness = 1.0;//金属度
obj.material.roughness = 0.3;//表面粗糙度
obj.material.envMap = textureCube;
obj.material.envMapIntensity = 1.0;
}
});
model.add(gltf.scene);
})
.environment(场景中scene所有Mesh)
如果你希望环境贴图影响场景中scene所有Mesh,可以通过Scene的场景环境属性.environment实现,把环境贴图对应纹理对象设置为.environment的属性值即可。
bash
// 环境贴图纹理对象textureCube作为.environment属性值,影响所有模型
scene.environment = textureCube;
四、MeshPhysicalMaterial
MeshPhysicalMaterial和MeshStandardMaterial都是拥有金属度metalness、粗糙度roughness属性的PBR材质。
MeshPhysicalMaterial是在MeshStandardMaterial基础上扩展出来的子类,除了继承了MeshStandardMaterial的金属度、粗糙度等属性,还新增了清漆.clearcoat、透光率.transmission、反射率.reflectivity、光泽.sheen、折射率.ior等等各种用于模拟生活中不同材质的属性。
1、清漆层属性.clearcoat
-
清漆层属性.clearcoat可以用来模拟物体表面一层透明图层,就好比你在物体表面刷了一层透明清漆,喷了点水。亮面的汽车被雨淋了的感觉。
-
.clearcoat的范围0到1,默认0。
-
通过下面的图直观体会一下清漆层的作用
未加清漆层

加了清漆层:可以反射一点环境贴图

-
完整代码
bash
// 引入Three.js
import * as THREE from 'three';
// 引入gltf模型加载库GLTFLoader.js
import {
GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader(); // *创建一个GLTF加载器
const model = new THREE.Group(); // *声明组对象,用于添加加载成功的三维场景
// *加载环境贴图
const textureCode = new THREE.CubeTextureLoader()
.setPath('../../环境贴图/环境贴图1/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
// *加载三维模型
loader.load("../../轿车.glb", function (gltf) {
model.add(gltf.scene);
console.log('gltf.scene',gltf.scene);
const mesh = gltf.scene.getObjectByName('外壳01');
mesh.material = new THREE.MeshPhysicalMaterial({
color: mesh.material.color, // *保持三维模型原有颜色
metalness: 0.9,// *金属度
roughness: 0.5,// *粗糙度
clearcoat: 1,// *清漆层
clearcoatRoughness: 0.01, // *清漆层粗糙度
envMap: textureCode, // *环境贴图
envMapIntensity: 2.5, // *环境贴图对Mesh表面影响程度
})
})
export default model;
2、清漆层粗糙度.clearcoatRoughness
-表示物体表面透明涂层.clearcoat对应的的粗糙度
- 范围是为0.0至1.0。默认值为0.0
3、清透层 - gui交互辅助调试
在设置车外壳清漆层之前,先创建一个MeshPhysicalMaterial材质,并设置好环境贴图、金属度、粗糙度,属性值先根据文档说明给一个大概的值,具体可以通过gui交互界面可视化调试。
(入门章有学gui这里就不祥述)
效果图

完整代码
bash
// 引入Three.js
import * as THREE from 'three';
// 引入gltf模型加载库GLTFLoader.js
import {
GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';
import gui from './gui.js';
// *gui - 创建材质子菜单
const matFolder = gui.addFolder('车外壳材质').close();
matFolder.close(); // *父级菜单关闭
const loader = new GLTFLoader(); // *创建一个GLTF加载器
const model = new THREE.Group(); // *声明组对象,用于添加加载成功的三维场景
// *加载环境贴图
const textureCode = new THREE.CubeTextureLoader()
.setPath('../../环境贴图/环境贴图1/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
// *加载三维模型
loader.load("../../轿车.glb", function (gltf) {
model.add(gltf.scene);
console.log('gltf.scene',gltf.scene);
const mesh = gltf.scene.getObjectByName('外壳01');
mesh.material = new THREE.MeshPhysicalMaterial({
color: mesh.material.color, // *保持三维模型原有颜色
metalness: 0.9,// *金属度
roughness: 0.5,// *粗糙度
clearcoat: 1,// *清漆层
clearcoatRoughness: 0.01, // *清漆层粗糙度
envMap: textureCode, // *环境贴图
envMapIntensity: 2.5, // *环境贴图对Mesh表面影响程度
})
const obj = {
color: mesh.material.color.getHex(), // *获取材质默认颜色
}
// *gui - 设置材质颜色、金属度、粗糙度、清漆层、清漆层粗糙度、环境贴图强度
matFolder.addColor(obj, 'color').onChange(function(value){
mesh.material.color.set(value)
});
matFolder.add(mesh.material, 'metalness', 0, 1);
matFolder.add(mesh.material, 'roughness', 0, 1);
matFolder.add(mesh.material, 'clearcoat', 0, 1);
matFolder.add(mesh.material, 'clearcoatRoughness', 0, 1);
matFolder.add(mesh.material, 'envMapIntensity', 0, 1);
})
export default model;
4、透光率(透射度).transmission
- 为了更好的模拟
玻璃、半透明塑料一类的视觉效果,可以使用物理透明度.transmission属性代替Mesh普通透明度属性.opacity。
使用.transmission属性设置Mesh透明度,即便完全透射的情况下仍可保持高反射率。
- 物理光学透明度.transmission的值范围是从0.0到1.0。默认值为0.0。
bash
const mesh = gltf.scene.getObjectByName('玻璃01')
mesh.material = new THREE.MeshPhysicalMaterial({
transmission: 1.0, //玻璃材质透光率,transmission替代opacity
})
5、折射率.ior
非金属材料的折射率从1.0到2.333。默认值为1.5。
不同材质的折射率,你可以百度搜索。
bash
new THREE.MeshPhysicalMaterial({
ior:1.5,//折射率
})
6、车前挡风玻璃效果示例

完整代码
bash
// 引入Three.js
import * as THREE from 'three';
// 引入gltf模型加载库GLTFLoader.js
import {
GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';
import gui from './gui.js';
// 创建材质子菜单
const matFolder1 = gui.addFolder('外壳材质');
matFolder1.close(); //关闭菜单
const matFolder2 = gui.addFolder('玻璃材质');
matFolder2.close(); //关闭菜单
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
// 加载环境贴图
const textureCube = new THREE.CubeTextureLoader()
.setPath('../../环境贴图/环境贴图1/')
.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
textureCube.encoding = THREE.sRGBEncoding; //设置纹理贴图编码方式和WebGL渲染器一致
loader.load("../车pbr.glb", function (gltf) {
model.add(gltf.scene);
// 注意如果车外壳或玻璃共享了材质,修改一个其他的也会变化和影响
const mesh1 = gltf.scene.getObjectByName('外壳01');
mesh1.material.envMap = textureCube; //环境贴图
mesh1.material.envMapIntensity = 1.0; ////环境贴图对Mesh表面影响程度
const mesh2 = gltf.scene.getObjectByName('玻璃01');
mesh2.material.envMap = textureCube; //环境贴图
mesh2.material.envMapIntensity = 1.0; ////环境贴图对Mesh表面影响程度
// 查看threejs解析的PBR材质
gltf.scene.traverse(function(obj) {
if (obj.isMesh) {
console.log('obj.material',obj.material);
}
});
console.log('外壳',mesh1.material);
console.log('玻璃',mesh2.material);
const obj = {
color1: mesh1.material.color.getHex(), // 外壳颜色
color2: mesh2.material.color.getHex(), // 玻璃颜色
};
// 车壳材质gui界面
matFolder1.addColor(obj, 'color1').onChange(function (value) {
mesh1.material.color.set(value);
});
matFolder1.add(mesh1.material, 'metalness', 0, 1);
matFolder1.add(mesh1.material, 'roughness', 0, 1);
matFolder1.add(mesh1.material, 'clearcoat', 0, 1);
matFolder1.add(mesh1.material, 'clearcoatRoughness', 0, 1);
matFolder1.add(mesh1.material, 'envMapIntensity', 0, 10);
// 玻璃材质gui界面
matFolder2.addColor(obj, 'color2').onChange(function (value) {
mesh2.material.color.set(value);
});
matFolder2.add(mesh2.material, 'metalness', 0, 1);
matFolder2.add(mesh2.material, 'roughness', 0, 1);
matFolder2.add(mesh2.material, 'transmission', 0, 1);
matFolder2.add(mesh2.material, 'ior', 1, 2.333);
matFolder2.add(mesh2.material, 'envMapIntensity', 0, 10);
})
export default model;
五、其他
实际开发的时候PBR材质的属性,很多时候是可以在三维建模软件中设置的,然后通过gltf导出即可,这样就不用在threejs代码设置。
通常美术对三维场景渲染的了解也比大部分前端程序员多的多,只要美术在三维建模软件设置好并导出包含pbr材质属性的gltf即可。