前面我们学过 MeshLambertMaterial、MeshPhongMaterial,它们都可以反射光线,但是不够逼真。
这节我们来学一种更强大的材质:PBR 材质。
PBR 是 Physically-based rendering,基于物理渲染,也就是光射到物体表面的反射、折射、吸收等物理现象,并且根据是否是金属、是否粗糙等属性来实现接近真实的效果。
PBR 对应的类是 MeshStandardMaterial 标准网格材质,为什么是 standard 呢?
文档里写了:

它已经成为许多 3D 应用的标准了。
当然,PBR 虽然可以实现更逼真的效果,但是计算成本更高。
我们来试一下:
npx create-vite pbr-material

进入项目,安装依赖:
npm install
npm install --save three
npm install --save-dev @types/three
改下 src/main.js
import './style.css';
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const directionLight = new THREE.DirectionalLight(0xffffff, 2);
directionLight.position.set(500, 400, 300);
scene.add(directionLight);
const ambientLight = new THREE.AmbientLight();
scene.add(ambientLight);
const width = window.innerWidth;
const height = window.innerHeight;
const helper = new THREE.AxesHelper(500);
// scene.add(helper);
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000);
camera.position.set(500, 600, 800);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(width, height)
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Light、Camera、Renderer。
改下 style.css
body {
margin: 0;
}
写下 mesh.js
import * as THREE from 'three';
const geometry = new THREE.CylinderGeometry(200, 200, 500);
const material = new THREE.MeshStandardMaterial({
color: 'orange'
});
const mesh = new THREE.Mesh(geometry, material);
export default mesh
画了一个圆柱。
先跑下:
npm run dev


如果这个圆柱比较光滑,当周围有物体的时候,它会反射出这些物体来。
这个用环境贴图 envMap 来实现。
我们找个天空盒,天空盒就是全景图切割成上下左右前后 6 张图。
网上搜一下全景图,然后用这个工具来切割成天空盒:
https://jaxry.github.io/panorama-to-cubemap/
这种看着比较扭曲的,就是全景图:

下载下来,点击选择文件:

下面这 6 张就是切割好的天空盒

点击 6 张图片,下载到本地:

x、y、z 就是 xyz 轴,然后 px 就是 positive x 正半轴,nx 是 negative x 负半轴。
放到 public 目录下:

然后代码里用 CubeTextureLoader 加载这 6 张图进来,作为天空盒纹理,设置到 secne.background

const textureCube = new THREE.CubeTextureLoader()
.setPath('./forest/')
.load(['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']);
scene.background = textureCube;
看下效果:

这样环境就设置好了。
在这样一个环境里,物体上完全看不到反射的环境,这合理么?
不合理,所以我们要设置下环境贴图 envMap

const textureCube = new THREE.CubeTextureLoader()
.setPath('./forest/')
.load(['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']);
const geometry = new THREE.CylinderGeometry(200, 200, 500);
const material = new THREE.MeshStandardMaterial({
color: 'orange',
roughness: 0,
metalness: 1,
envMap: textureCube,
envMapIntensity: 1
});
设置 envMap 环境贴图
roughness 是粗糙度,设置为 0 就是完全光滑的镜面
metalness 是金属度,设置为 1 就是完全的金属效果
看下效果:

现在就能反射环境了,并且有种金属的质感:

是不是很逼真?
我们用 GUI 可视化调节下这几个参数试试:

const gui = new GUI();
gui.addColor(material, 'color');
gui.add(material, 'roughness', 0, 1);
gui.add(material, 'metalness', 0, 1);
gui.add(material, 'envMapIntensity', 0, 5);
调大粗糙度 roughness 看一下:

可以看到,粗糙度调大后就没有镜面的感觉效果了。
再调小下金属度 metalness:

金属度调小后就变得像塑料了。
再来调节下 envMapIntensity:

这个是受环境影响的强度,一般设置 1 就行。
这就是 PBR 材质的金属度 metalness、粗糙度 roughness、环境贴图 envMap,它们配合可以实现各种材质效果。
案例代码上传了小册仓库。
总结
这节我们学了 PBR 材质,它是基于物理渲染的材质,会做更多的计算,实现非常逼真的效果。
PBR 材质是 MeshStandardMaterial 标准网格材质,通过设置 roughness 粗糙度、metalness 金属度、envMap 环境贴图,可以实现逼真的金属、塑料、磨砂等材质效果。
此外,PBR 材质还可以实现玻璃、喷漆等效果,下节我们继续学习 PBR。