threejs(七)PBR材质

材质 属性 含义 使用方法 范围及效果
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材质相关的APIMeshStandardMaterialMeshPhysicalMaterial,MeshPhysicalMaterial 是 MeshStandardMaterial 扩展的子类,提供了更多功能属性。

网格模型材质

MeshLambertMaterial: Lambert光照模型(漫反射)

MeshPhongMaterial:Phong光照模型(漫反射、高光反射)

MeshStandardMaterial和MeshPhysicalMaterial:基于物理的光照模型(微平面理论、能量守恒、菲涅尔反射...)

渲染占用资源和表现能力

PBR材质相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的、更接近生活中的材质效果,当然也会占用更多的电脑硬件资源。

渲染表现能力越强,占用的计算机硬件资源更多。

占用渲染资源 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial

渲染表现能力 MeshBasicMaterial < MeshLambertMaterial < MeshPhongMaterial < MeshStandardMaterial < MeshPhysicalMaterial

二、PBR材质的常用属性

MeshStandardMaterial属性

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返回Texture
  • CubeTextureLoader返回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

MeshPhysicalMaterialMeshStandardMaterial都是拥有金属度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即可。

相关推荐
zh_xuan5 小时前
c++ stringstream字符串流的用法
开发语言·c++
小王不爱笑1325 小时前
Java 核心知识点查漏补缺(二)
java·开发语言
Lacrimosa&L5 小时前
OS_2 进程与线程(进程管理)
java·开发语言
zzywxc7875 小时前
解锁 Rust 开发新可能:从系统内核到 Web 前端的全栈革命
开发语言·前端·python·单片机·嵌入式硬件·rust·scikit-learn
大雨淅淅5 小时前
【编程语言】Rust 入门
开发语言·后端·rust
桃花键神5 小时前
【送书福利-第四十四期】《 深入Rust标准库》
开发语言·后端·rust
像风一样自由20205 小时前
使用Rust构建高性能文件搜索工具
开发语言·后端·rust
墨咖5 小时前
java实现NTP服务以及服务调用端(Client)功能
java·开发语言·时间同步·ntp·时钟源同步
敲敲了个代码5 小时前
UniApp 多页面编译优化:编译时间从10分钟到1分钟
开发语言·前端·javascript·学习·uni-app