基于物理的渲染(Physically Based Rendering)是一种遵循真实世界物理规律的光照模型,通过模拟光线与物体表面的物理交互来生成逼真的渲染效果。
能量守恒:反射光 + 折射光 ≤ 入射光
微表面理论:物体表面由许多微观平面组成
菲涅尔效应:不同观察角度下的反射率变化
javascript
// 基本使用
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.5, // 粗糙度:0(镜面)~1(完全粗糙)
metalness: 0.5, // 金属度:0(非金属)~1(金属)
map: texture, // 漫反射贴图
normalMap: normalTexture, // 法线贴图
roughnessMap: roughnessTexture, // 粗糙度贴图
metalnessMap: metalnessTexture, // 金属度贴图
aoMap: aoTexture, // 环境光遮蔽贴图
emissive: 0x000000, // 自发光颜色
emissiveIntensity: 1 // 自发光强度
});
在标准材质基础上增加了更多物理属性:
javascript
const material = new THREE.MeshPhysicalMaterial({
// 继承所有MeshStandardMaterial属性
clearcoat: 0.5, // 清漆层强度:0~1
clearcoatRoughness: 0.1, // 清漆层粗糙度
sheen: 0.5, // 光泽层(织物材质)
sheenRoughness: 0.5,
transmission: 0.5, // 透射度(透明材质)
thickness: 0.1, // 厚度(用于透射计算)
ior: 1.5, // 折射率
specularIntensity: 1, // 镜面反射强度
specularColor: 0xffffff, // 镜面反射颜色
});
javascript
const textureLoader = new THREE.TextureLoader();
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
// 基础贴图
map: textureLoader.load('albedo.jpg'), // 反照率/漫反射
// 物理属性贴图
roughnessMap: textureLoader.load('roughness.jpg'),
metalnessMap: textureLoader.load('metalness.jpg'),
// 细节增强贴图
normalMap: textureLoader.load('normal.jpg'), // 法线贴图
normalScale: new THREE.Vector2(1, 1), // 法线强度
aoMap: textureLoader.load('ao.jpg'), // 环境光遮蔽
aoMapIntensity: 1, // AO强度
displacementMap: textureLoader.load('height.png'), // 置换贴图
displacementScale: 0.1,
displacementBias: -0.05,
// 自发光
emissiveMap: textureLoader.load('emissive.jpg'),
emissive: 0xffffff,
emissiveIntensity: 1
});
将多个参数打包到一张贴图的不同通道:
javascript
// RGBA通道分别存储不同信息
// R: 粗糙度, G: 金属度, B: AO, A: 高度
const packedMap = textureLoader.load('packed.jpg');
material.roughnessMap = packedMap;
material.metalnessMap = packedMap;
material.aoMap = packedMap;
// 设置不同的通道
material.roughnessMap.channel = 0; // R通道
material.metalnessMap.channel = 1; // G通道
material.aoMap.channel = 2; // B通道
javascript
// 1. 加载HDR环境贴图
new THREE.RGBELoader()
.load('environment.hdr', function(texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
// 设置为场景环境
scene.environment = texture;
// 设置为材质的背景
scene.background = texture;
});
// 2. 创建环境贴图
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envTexture = pmremGenerator.fromScene(environmentScene).texture;
material.envMap = envTexture;
material.envMapIntensity = 1.0;
javascript
// HDRI环境光照 + 方向光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 或者使用光探针
const lightProbe = new THREE.LightProbe();
scene.add(lightProbe);
// 从环境贴图生成光探针
pmremGenerator.fromScene(environmentScene).then((cubeRenderTarget) => {
lightProbe.copy(LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget));
});
javascript
// 1. 使用压缩纹理格式
const compressedTexture = new THREE.CompressedTextureLoader()
.setPath('textures/')
.load('diffuse.dds');
// 2. 调整精度
material.precision = 'mediump'; // 'highp', 'mediump', 'lowp'
// 3. 合并材质参数
material.combine = THREE.MultiplyOperation; // 合并方式
javascript
// 1. 使用PBR优化渲染器
renderer.physicallyCorrectLights = true;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
// 2. 调整渲染质量
renderer.outputEncoding = THREE.sRGBEncoding;
javascript
// 创建PBR材质的完整示例
function createPBRMaterial() {
const textureLoader = new THREE.TextureLoader();
// 加载所有贴图
const albedo = textureLoader.load('textures/albedo.jpg');
const normal = textureLoader.load('textures/normal.jpg');
const roughness = textureLoader.load('textures/roughness.jpg');
const metalness = textureLoader.load('textures/metalness.jpg');
const ao = textureLoader.load('textures/ao.jpg');
// 创建PBR材质
const material = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
map: albedo,
normalMap: normal,
normalScale: new THREE.Vector2(1, 1),
roughnessMap: roughness,
roughness: 0.5,
metalnessMap: metalness,
metalness: 0.8,
aoMap: ao,
aoMapIntensity: 1,
// 物理特性
clearcoat: 0.2,
clearcoatRoughness: 0.1,
ior: 1.5,
transmission: 0,
// 性能设置
side: THREE.FrontSide,
transparent: false,
alphaTest: 0.5
});
// 设置贴图重复和包装
[albedo, normal, roughness, metalness, ao].forEach(texture => {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
});
return material;
}
// 创建物体
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = createPBRMaterial();
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// 设置环境
new THREE.RGBELoader()
.load('environment.hdr', function(texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
material.envMap = texture;
material.envMapIntensity = 1.0;
});
javascript
// 增加环境光强度
material.envMapIntensity = 2.0;
// 或调整曝光
renderer.toneMappingExposure = 1.5;
// 或添加补光
const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);
javascript
// 调整粗糙度
material.roughness = 0.8;
// 降低环境贴图强度
material.envMapIntensity = 0.5;
// 使用菲涅尔衰减
material.specularIntensity = 0.5;
javascript
// 减少贴图分辨率
textureLoader.load('texture.jpg', texture => {
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.generateMipmaps = true;
});
// 使用更简单的材质
material.precision = 'mediump';
使用正确的贴图格式:HDR用于环境,PNG/JPG用于材质
保持能量守恒:高金属度配合低粗糙度,非金属反之
合理使用法线贴图:避免过度使用导致视觉失真
优化贴图分辨率:根据物体在画面中的大小决定
统一光照单位:使用物理正确的光照强度和单位
通过合理配置PBR材质参数和贴图,配合适当的光照环境,可以在Three.js中实现高度逼真的渲染效果。