1. Three.js动态环境贴图

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();

WebGLCubeRenderTarget 的重要参数

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
  }
);

关键点总结

  1. WebGLCubeRenderTarget 是专门用于立方体贴图的渲染目标

  2. 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
    };
    // 自定义着色器代码混合贴图...
  };
});

Progressive 更新

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++;
  }
}

 性能注意事项

  1. 分辨率控制:256x256 通常是质量与性能的平衡点

  2. 更新频率:静态场景可降低更新频率

  3. 渲染目标重用:多个物体共享同一个环境贴图

  4. 视锥体剔除:只渲染视野内的物体到环境贴图

 替代方案

对于需要高性能的场景,考虑:

动态环境贴图是 Three.js 高级渲染技术,合理使用可以显著提升场景真实感,但需要平衡性能开销。