1. 常用效果:辉光、景深、色彩校正

Three.js 中实现辉光、景深和色彩校正这三种常用效果的方法:

1.1. 辉光效果 (Glow/Bloom)

1.1.1. 后处理实现(推荐)

javascript

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';

// 创建后处理合成器
const composer = new EffectComposer(renderer);

// 添加渲染通道
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

// 创建辉光效果
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,    // 强度
  0.4,    // 半径
  0.85    // 阈值
);
composer.addPass(bloomPass);

// 可选:添加抗锯齿
const fxaaPass = new ShaderPass(FXAAShader);
composer.addPass(fxaaPass);

// 在动画循环中使用
function animate() {
  composer.render();
}

1.1.2. 特定物体辉光(选择性辉光)

javascript

1.1.2.1. 方法1:使用多层渲染和 MaskPass(推荐)

javascript

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { MaskPass, ClearMaskPass } from 'three/examples/jsm/postprocessing/MaskPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js';

const composer = new EffectComposer(renderer);

// 1. 渲染所有物体(无辉光)
const renderScene = new RenderPass(scene, camera);
composer.addPass(renderScene);

// 2. 创建辉光层
const bloomComposer = new EffectComposer(renderer);
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,    // 强度
  0.4,    // 半径
  0.85    // 阈值
);

// 3. 设置需要辉光的物体
const glowLayer = new THREE.Layers();
glowLayer.set(1); // 使用第1层

// 需要辉光的物体添加到第1层
const glowObjects = []; // 存储需要辉光的物体
glowObjects.forEach(obj => {
  obj.layers.enable(1);
});

// 4. 创建只渲染辉光物体的场景
const bloomScene = new THREE.Scene();
bloomScene.copy(scene); // 复制原场景
bloomScene.children = bloomScene.children.filter(child => 
  glowObjects.includes(child) || child.layers.test(glowLayer)
);

const renderBloomScene = new RenderPass(bloomScene, camera);
bloomComposer.addPass(renderBloomScene);
bloomComposer.addPass(bloomPass);

// 5. 合并两个渲染结果
const finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderScene);

// 添加辉光层的遮罩
const maskPass = new MaskPass(scene, camera);
maskPass.inverse = true; // 反转遮罩
finalComposer.addPass(maskPass);

// 添加辉光
finalComposer.addPass(new ShaderPass(CopyShader));
finalComposer.addPass(new ClearMaskPass());

// 渲染
function animate() {
  // 先渲染辉光层
  bloomComposer.render();

  // 再渲染最终合成
  finalComposer.render();
}
1.1.2.2. 方法2:使用自定义渲染目标和混合

javascript

class SelectiveBloom {
  constructor(renderer, scene, camera) {
    this.renderer = renderer;
    this.scene = scene;
    this.camera = camera;

    // 创建辉光合成器
    this.bloomComposer = new EffectComposer(renderer);

    // 基础场景渲染
    this.renderPass = new RenderPass(scene, camera);
    this.bloomComposer.addPass(this.renderPass);

    // 辉光效果
    this.bloomPass = new UnrealBloomPass(
      new THREE.Vector2(window.innerWidth, window.innerHeight),
      1.5, 0.4, 0.85
    );
    this.bloomComposer.addPass(this.bloomPass);

    // 创建纹理用于辉光物体
    this.bloomTarget = new THREE.WebGLRenderTarget(
      window.innerWidth, 
      window.innerHeight
    );

    // 需要辉光的物体列表
    this.glowObjects = [];

    // 创建用于混合辉光的材质
    this.blendMaterial = new THREE.ShaderMaterial({
      uniforms: {
        baseTexture: { value: null },
        bloomTexture: { value: this.bloomTarget.texture },
        intensity: { value: 1.0 }
      },
      vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform sampler2D baseTexture;
        uniform sampler2D bloomTexture;
        uniform float intensity;
        varying vec2 vUv;

        void main() {
          vec4 baseColor = texture2D(baseTexture, vUv);
          vec4 bloomColor = texture2D(bloomTexture, vUv);

          // 混合辉光
          gl_FragColor = baseColor + bloomColor * intensity;
        }
      `
    });
  }

  addGlowObject(object) {
    this.glowObjects.push(object);

    // 为物体创建副本用于辉光渲染
    const glowMaterial = new THREE.MeshBasicMaterial({
      color: object.material.color || 0xffffff,
      emissive: object.material.emissive || 0x000000
    });

    const glowMesh = new THREE.Mesh(object.geometry, glowMaterial);
    glowMesh.position.copy(object.position);
    glowMesh.rotation.copy(object.rotation);
    glowMesh.scale.copy(object.scale);
    this.glowScene.add(glowMesh);
  }

  render() {
    // 1. 先渲染辉光物体到单独的目标
    this.renderer.setRenderTarget(this.bloomTarget);
    this.renderer.clear();

    // 只渲染辉光物体
    this.glowObjects.forEach(obj => {
      const originalVisible = obj.visible;
      obj.visible = true;

      // 临时更改材质使其在辉光通道中更亮
      const originalMaterial = obj.material;
      const glowMaterial = new THREE.MeshBasicMaterial({
        color: obj.material.color,
        emissive: obj.material.emissive || 0x000000
      });
      obj.material = glowMaterial;

      this.renderer.render(obj, this.camera);

      // 恢复原状
      obj.material = originalMaterial;
      obj.visible = originalVisible;
    });

    // 2. 渲染整个场景
    this.renderer.setRenderTarget(null);
    this.renderer.render(this.scene, this.camera);

    // 3. 应用辉光
    this.blendMaterial.uniforms.baseTexture.value = 
      this.renderer.getContext().canvas;

    const quad = new THREE.Mesh(
      new THREE.PlaneGeometry(2, 2),
      this.blendMaterial
    );

    const scene = new THREE.Scene();
    scene.add(quad);
    const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);

    this.renderer.render(scene, camera);
  }
}
1.1.2.3. 方法3:使用后处理的简化方法

javascript

// 使用多个渲染通道来实现选择性辉光
function createSelectiveBloom(scene, camera, renderer) {
  const bloomLayer = 1;

  // 主合成器
  const finalComposer = new EffectComposer(renderer);

  // 1. 渲染非辉光物体
  const normalRender = new RenderPass(scene, camera);
  finalComposer.addPass(normalRender);

  // 2. 辉光合成器
  const bloomComposer = new EffectComposer(renderer);

  // 创建辉光场景(只包含辉光物体)
  const bloomScene = new THREE.Scene();
  const bloomCamera = camera.clone();

  const bloomRenderPass = new RenderPass(bloomScene, bloomCamera);
  bloomComposer.addPass(bloomRenderPass);

  const bloomPass = new UnrealBloomPass(
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    1.5, 0.4, 0.85
  );
  bloomComposer.addPass(bloomPass);

  // 3. 混合辉光
  const mixPass = new ShaderPass(
    new THREE.ShaderMaterial({
      uniforms: {
        baseTexture: { value: null },
        bloomTexture: { value: null },
        mixRatio: { value: 0.8 }
      },
      vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform sampler2D baseTexture;
        uniform sampler2D bloomTexture;
        uniform float mixRatio;
        varying vec2 vUv;

        void main() {
          vec4 base = texture2D(baseTexture, vUv);
          vec4 bloom = texture2D(bloomTexture, vUv);
          gl_FragColor = base + bloom * mixRatio;
        }
      `,
      transparent: true
    })
  );

  return {
    addGlowObject: function(obj) {
      // 将物体克隆到辉光场景
      const clone = obj.clone();
      clone.layers.set(bloomLayer);
      bloomScene.add(clone);
    },
    render: function() {
      // 先渲染辉光
      bloomComposer.render();

      // 设置纹理
      mixPass.uniforms.baseTexture.value = 
        finalComposer.renderTarget2.texture;
      mixPass.uniforms.bloomTexture.value = 
        bloomComposer.renderTarget2.texture;

      // 渲染最终结果
      finalComposer.render();
    }
  };
}

1.2. 景深效果 (Depth of Field)

1.2.1. 使用后处理实现

javascript

import { BokehPass } from 'three/examples/jsm/postprocessing/BokehPass.js';

// 创建景深效果
const bokehPass = new BokehPass(scene, camera, {
  focus: 10.0,      // 焦点距离
  aperture: 0.025,  // 光圈大小
  maxblur: 0.01     // 最大模糊度
});

// 添加到合成器
composer.addPass(bokehPass);

// 动态调整焦点
function updateFocus(targetDistance) {
  bokehPass.uniforms['focus'].value = targetDistance;
  bokehPass.uniforms['aperture'].value = 
    targetDistance * 0.0005; // 根据距离调整光圈
}

1.2.2. 自定义景深效果

javascript

import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';

const DepthOfFieldShader = {
  uniforms: {
    'tDiffuse': { value: null },
    'tDepth': { value: null },
    'focus': { value: 1.0 },
    'aperture': { value: 0.025 },
    'maxblur': { value: 1.0 }
  },
  // ... 着色器代码
};

const dofPass = new ShaderPass(DepthOfFieldShader);
composer.addPass(dofPass);

1.3. 色彩校正 (Color Correction)

1.3.1. 使用后处理调色

javascript

import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { ColorCorrectionShader } from 'three/examples/jsm/shaders/ColorCorrectionShader.js';

const colorCorrectionPass = new ShaderPass(ColorCorrectionShader);

// 调整参数
colorCorrectionPass.uniforms['powRGB'].value = 
  new THREE.Vector3(1.2, 1.2, 1.2); // 幂次校正
colorCorrectionPass.uniforms['mulRGB'].value = 
  new THREE.Vector3(1.1, 1.0, 0.9); // 乘法校正

composer.addPass(colorCorrectionPass);

1.3.2. LUT(颜色查找表)调色

javascript

import { LUTPass } from 'three/examples/jsm/postprocessing/LUTPass.js';
import { LUTCubeLoader } from 'three/examples/jsm/loaders/LUTCubeLoader.js';

// 加载 LUT 文件
const loader = new LUTCubeLoader();
loader.load('path/to/lut.cube', (result) => {
  const lutPass = new LUTPass({
    lut: result.texture3D,
    intensity: 1.0
  });
  composer.addPass(lutPass);
});

1.3.3. 实时颜色调整

javascript

// 创建自定义颜色校正着色器
const ColorAdjustShader = {
  uniforms: {
    'tDiffuse': { value: null },
    'brightness': { value: 0.0 },
    'contrast': { value: 1.0 },
    'saturation': { value: 1.0 },
    'exposure': { value: 1.0 },
    'gamma': { value: 2.2 }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D tDiffuse;
    uniform float brightness;
    uniform float contrast;
    uniform float saturation;
    uniform float exposure;
    uniform float gamma;

    varying vec2 vUv;

    void main() {
      vec4 color = texture2D(tDiffuse, vUv);

      // 亮度调整
      color.rgb += brightness;

      // 对比度调整
      color.rgb = ((color.rgb - 0.5) * max(contrast, 0.0)) + 0.5;

      // 饱和度调整
      float luminance = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
      color.rgb = mix(vec3(luminance), color.rgb, saturation);

      // 曝光调整
      color.rgb *= exposure;

      // Gamma校正
      color.rgb = pow(color.rgb, vec3(1.0 / gamma));

      gl_FragColor = color;
    }
  `
};

const colorAdjustPass = new ShaderPass(ColorAdjustShader);
composer.addPass(colorAdjustPass);

// 实时调整参数
gui.add(colorAdjustPass.uniforms.brightness, 'value', -0.5, 0.5).name('亮度');
gui.add(colorAdjustPass.uniforms.contrast, 'value', 0.5, 2.0).name('对比度');
gui.add(colorAdjustPass.uniforms.saturation, 'value', 0.0, 2.0).name('饱和度');

1.4. 综合使用示例

javascript

// 创建完整的后处理管道
class PostProcessingPipeline {
  constructor(renderer, scene, camera) {
    this.composer = new EffectComposer(renderer);

    // 基础渲染
    this.composer.addPass(new RenderPass(scene, camera));

    // 辉光效果
    this.bloomPass = new UnrealBloomPass(
      new THREE.Vector2(window.innerWidth, window.innerHeight),
      1.2, 0.4, 0.85
    );
    this.composer.addPass(this.bloomPass);

    // 景深效果
    this.bokehPass = new BokehPass(scene, camera, {
      focus: 15.0,
      aperture: 0.02,
      maxblur: 0.005
    });
    this.composer.addPass(this.bokehPass);

    // 色彩校正
    this.colorPass = new ShaderPass(ColorAdjustShader);
    this.composer.addPass(this.colorPass);

    // 抗锯齿
    this.fxaaPass = new ShaderPass(FXAAShader);
    this.composer.addPass(this.fxaaPass);
  }

  setSize(width, height) {
    this.composer.setSize(width, height);
  }

  render() {
    this.composer.render();
  }
}

// 使用
const pipeline = new PostProcessingPipeline(renderer, scene, camera);

function animate() {
  pipeline.render();
  requestAnimationFrame(animate);
}

1.5. 性能优化建议

javascript

// 1. 降低分辨率以提高性能
composer.setSize(window.innerWidth * 0.5, window.innerHeight * 0.5);

// 2. 仅在需要时启用效果
function toggleEffects(enabled) {
  bloomPass.enabled = enabled;
  bokehPass.enabled = enabled;
}

// 3. 使用低质量预设
const lowQualityBloom = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2),
  1.0, 0.2, 0.6
);

// 4. 动态调整质量
function adjustQualityBasedOnFPS(fps) {
  if (fps < 30) {
    this.bloomPass.strength = 0.8;
    this.bokehPass.maxblur = 0.002;
  } else {
    this.bloomPass.strength = 1.5;
    this.bokehPass.maxblur = 0.01;
  }
}

这些效果可以显著提升 Three.js 应用的视觉质量。建议根据具体需求选择合适的效果组合,并注意性能优化。