Skip to content

61. PBR 材质:逼真的金属、塑料、磨砂效果

Published:

前面我们学过 MeshLambertMaterial、MeshPhongMaterial,它们都可以反射光线,但是不够逼真。

这节我们来学一种更强大的材质:PBR 材质。

PBR 是 Physically-based rendering,基于物理渲染,也就是光射到物体表面的反射、折射、吸收等物理现象,并且根据是否是金属、是否粗糙等属性来实现接近真实的效果。

PBR 对应的类是 MeshStandardMaterial 标准网格材质,为什么是 standard 呢?

文档里写了:

image.png

它已经成为许多 3D 应用的标准了。

当然,PBR 虽然可以实现更逼真的效果,但是计算成本更高。

我们来试一下:

npx create-vite pbr-material

image.png

进入项目,安装依赖:

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

image.png

2025-04-09 16.12.40.gif

如果这个圆柱比较光滑,当周围有物体的时候,它会反射出这些物体来。

这个用环境贴图 envMap 来实现。

我们找个天空盒,天空盒就是全景图切割成上下左右前后 6 张图。

网上搜一下全景图,然后用这个工具来切割成天空盒:

https://jaxry.github.io/panorama-to-cubemap/

这种看着比较扭曲的,就是全景图:

image.png

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

image.png

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

image.png

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

image.png

x、y、z 就是 xyz 轴,然后 px 就是 positive x 正半轴,nx 是 negative x 负半轴。

放到 public 目录下:

image.png

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

image.png

const textureCube = new THREE.CubeTextureLoader()
    .setPath('./forest/')
    .load(['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']);

scene.background = textureCube;

看下效果:

2025-04-09 19.06.31.gif

这样环境就设置好了。

在这样一个环境里,物体上完全看不到反射的环境,这合理么?

不合理,所以我们要设置下环境贴图 envMap

image.png

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 就是完全的金属效果

看下效果:

2025-04-09 19.16.46.gif

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

image.png

是不是很逼真?

我们用 GUI 可视化调节下这几个参数试试:

image.png

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 看一下:

2025-04-09 19.22.28.gif

可以看到,粗糙度调大后就没有镜面的感觉效果了。

再调小下金属度 metalness:

2025-04-09 19.23.11.gif

金属度调小后就变得像塑料了。

再来调节下 envMapIntensity:

2025-04-09 19.23.47.gif

这个是受环境影响的强度,一般设置 1 就行。

这就是 PBR 材质的金属度 metalness、粗糙度 roughness、环境贴图 envMap,它们配合可以实现各种材质效果。

案例代码上传了小册仓库

总结

这节我们学了 PBR 材质,它是基于物理渲染的材质,会做更多的计算,实现非常逼真的效果。

PBR 材质是 MeshStandardMaterial 标准网格材质,通过设置 roughness 粗糙度、metalness 金属度、envMap 环境贴图,可以实现逼真的金属、塑料、磨砂等材质效果。

此外,PBR 材质还可以实现玻璃、喷漆等效果,下节我们继续学习 PBR。

评论