Three.js 中动态环境贴图(Dynamic Environment Maps)是创建逼真反射/折射效果的关键技术。以下是详细实现方案:
动态环境贴图实时捕捉3D场景作为纹理,用于物体表面的环境反射。
// 核心步骤(只有这一种方法):
// 1. 创建 WebGLCubeRenderTarget
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256);
// 2. 创建 CubeCamera 并传入渲染目标
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
// 3. 使用 cubeRenderTarget.texture 作为环境贴图
const material = new THREE.MeshStandardMaterial({
envMap: cubeRenderTarget.texture
});
javascript
import * as THREE from 'three';
class DynamicEnvironmentMap {
constructor(renderer, scene, options = {}) {
this.renderer = renderer;
this.scene = scene;
// 配置参数
this.resolution = options.resolution || 256;
this.updateInterval = options.updateInterval || 30; // 帧间隔
this.near = options.near || 0.1;
this.far = options.far || 1000;
// 动态环境贴图的核心组件
this.cubeRenderTarget = null;
this.cubeCamera = null;
// 需要反射的物体
this.reflectiveObjects = new Set();
this.init();
}
init() {
// 步骤1:创建立方体贴图渲染目标
this.cubeRenderTarget = new THREE.WebGLCubeRenderTarget(this.resolution, {
format: THREE.RGBAFormat,
generateMipmaps: true,
minFilter: THREE.LinearMipmapLinearFilter,
encoding: THREE.sRGBEncoding
});
// 步骤2:创建立方体相机
this.cubeCamera = new THREE.CubeCamera(this.near, this.far, this.cubeRenderTarget);
this.scene.add(this.cubeCamera);
console.log('动态环境贴图初始化完成');
console.log(`分辨率: ${this.resolution}x${this.resolution}`);
console.log(`渲染目标类型:`, this.cubeRenderTarget.constructor.name);
}
// 添加需要反射的物体
addReflectiveObject(object, materialOptions = {}) {
// 确保物体有材质
if (!object.material) {
console.warn('物体没有材质,跳过');
return;
}
// 如果物体是 Mesh,直接使用其材质
if (object.material) {
// 设置环境贴图
object.material.envMap = this.cubeRenderTarget.texture;
object.material.needsUpdate = true;
// 应用材质参数
if (materialOptions.roughness !== undefined) {
object.material.roughness = materialOptions.roughness;
}
if (materialOptions.metalness !== undefined) {
object.material.metalness = materialOptions.metalness;
}
if (materialOptions.envMapIntensity !== undefined) {
object.material.envMapIntensity = materialOptions.envMapIntensity;
}
this.reflectiveObjects.add(object);
}
}
// 创建专门用于反射的材质
createReflectiveMaterial(options = {}) {
return new THREE.MeshStandardMaterial({
color: options.color || 0xffffff,
metalness: options.metalness || 0.95,
roughness: options.roughness || 0.1,
envMap: this.cubeRenderTarget.texture, // 核心:使用动态环境贴图
envMapIntensity: options.envMapIntensity || 1.0,
side: options.side || THREE.FrontSide
});
}
// 更新环境贴图
update(position = null) {
// 如果指定了位置,使用该位置;否则使用默认位置
const cameraPosition = position || new THREE.Vector3(0, 0, 0);
this.cubeCamera.position.copy(cameraPosition);
// 临时隐藏所有反射物体(避免自包含问题)
const originalVisibility = [];
this.reflectiveObjects.forEach(obj => {
originalVisibility.push(obj.visible);
obj.visible = false;
});
// 核心:更新立方体贴图(渲染6个方向)
this.cubeCamera.update(this.renderer, this.scene);
// 恢复物体可见性
let i = 0;
this.reflectiveObjects.forEach(obj => {
obj.visible = originalVisibility[i];
i++;
});
}
// 在渲染循环中调用
updateInRenderLoop(frameCount, cameraPosition = null) {
if (frameCount % this.updateInterval === 0) {
this.update(cameraPosition);
}
}
// 更改分辨率(需要重新初始化)
setResolution(resolution) {
this.resolution = resolution;
// 移除旧的相机
this.scene.remove(this.cubeCamera);
// 创建新的渲染目标和相机
this.cubeRenderTarget.dispose();
this.init();
// 重新为所有反射物体设置环境贴图
this.reflectiveObjects.forEach(obj => {
if (obj.material) {
obj.material.envMap = this.cubeRenderTarget.texture;
obj.material.needsUpdate = true;
}
});
}
// 清理资源
dispose() {
this.scene.remove(this.cubeCamera);
this.cubeRenderTarget.dispose();
this.reflectiveObjects.clear();
}
}
javascript
// 初始化场景
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建动态环境贴图管理器
const envMap = new DynamicEnvironmentMap(renderer, scene, {
resolution: 256,
updateInterval: 30
});
// 创建反射物体
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = envMap.createReflectiveMaterial({
metalness: 0.9,
roughness: 0.05
});
const reflectiveSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
reflectiveSphere.position.set(0, 1, 0);
scene.add(reflectiveSphere);
// 添加反射物体到管理器
envMap.addReflectiveObject(reflectiveSphere);
// 创建一些环境物体
function createEnvironment() {
// 地面
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshStandardMaterial({ color: 0x808080 })
);
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
// 彩色立方体
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
colors.forEach((color, i) => {
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color })
);
const angle = (i / colors.length) * Math.PI * 2;
cube.position.set(
Math.cos(angle) * 4,
0.5,
Math.sin(angle) * 4
);
scene.add(cube);
});
}
createEnvironment();
// 渲染循环
let frameCount = 0;
function animate() {
requestAnimationFrame(animate);
// 更新环境贴图(基于球体位置)
envMap.updateInRenderLoop(frameCount, reflectiveSphere.position);
// 旋转球体
reflectiveSphere.rotation.y += 0.01;
renderer.render(scene, camera);
frameCount++;
}
animate();
javascript
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(
256, // 分辨率
{
// 纹理格式
format: THREE.RGBAFormat, // 或 THREE.RGBFormat, THREE.RGBA16F等
// 纹理类型
type: THREE.UnsignedByteType, // 或 THREE.HalfFloatType, THREE.FloatType
// 是否生成mipmaps
generateMipmaps: true,
// 缩小过滤器
minFilter: THREE.LinearMipmapLinearFilter, // 有mipmaps时使用
// 放大过滤器
magFilter: THREE.LinearFilter,
// 编码方式
encoding: THREE.sRGBEncoding, // 或 THREE.LinearEncoding
// 深度缓冲区
depthBuffer: true,
stencilBuffer: false
}
);
WebGLCubeRenderTarget 是专门用于立方体贴图的渲染目标
CubeCamera 需要 WebGLCubeRenderTarget 来存储6个面的渲染结果
javascript
let updateEnvMap = true;
let frameCount = 0;
function updateEnvironmentMap() {
if (!updateEnvMap) return;
frameCount++;
// 每N帧更新一次
if (frameCount % 30 === 0) {
reflectiveObject.visible = false;
cubeCamera.update(renderer, scene);
reflectiveObject.visible = true;
}
}
javascript
// 使用静态HDR贴图作为基础
const hdrLoader = new RGBELoader();
hdrLoader.load('environment.hdr', function(texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
// 创建混合材质
material.envMap = texture;
material.defines = {
USE_ENVMAP: ''
};
// 添加动态更新
material.onBeforeCompile = (shader) => {
shader.uniforms.tDynamicEnvMap = {
value: cubeRenderTarget.texture
};
// 自定义着色器代码混合贴图...
};
});
javascript
class ProgressiveCubeCamera {
constructor(resolution, scene, renderer) {
this.currentFace = 0;
this.faces = [0, 1, 2, 3, 4, 5]; // +x, -x, +y, -y, +z, -z
this.cubeCamera = new THREE.CubeCamera(0.1, 1000, renderTarget);
}
update() {
// 每帧只更新一个面
const face = this.faces[this.currentFace % 6];
this.cubeCamera.updateFace(renderer, scene, face);
this.currentFace++;
}
}
分辨率控制:256x256 通常是质量与性能的平衡点
更新频率:静态场景可降低更新频率
渲染目标重用:多个物体共享同一个环境贴图
视锥体剔除:只渲染视野内的物体到环境贴图
对于需要高性能的场景,考虑:
预计算环境贴图
屏幕空间反射(SSR)
平面反射(用于水面、地板)
动态环境贴图是 Three.js 高级渲染技术,合理使用可以显著提升场景真实感,但需要平衡性能开销。