1. EffectComposer 工作流程详解

EffectComposer 是 Three.js 中用于实现后期处理效果的核心类,它允许你将多个渲染效果组合在一起,创建复杂的视觉特效。

1.1. 基本概念

1.1.1. 什么是 EffectComposer

EffectComposer 是一个管理后期处理通道(passes)的容器,它按照顺序执行这些通道,最终将结果渲染到屏幕或渲染目标。

1.1.2. 核心组件

1.2. 基本工作流程

1.2.1. 安装与导入

javascript

// 需要引入后期处理相关库
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js';

1.2.2. 基础设置流程

javascript

// 1. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);

// 2. 创建 EffectComposer
const composer = new EffectComposer(renderer);

// 3. 创建基础渲染通道
const renderPass = new RenderPass(scene, camera);

// 4. 添加通道到 composer
composer.addPass(renderPass);

// 5. 渲染循环中使用 composer 而不是 renderer
function animate() {
    requestAnimationFrame(animate);
    composer.render();  // 替代 renderer.render(scene, camera)
}

1.3. 完整工作流程示例

1.3.1. 完整代码结构

javascript

// 导入所需模块
import * as THREE from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';

class PostProcessingDemo {
    constructor() {
        this.initScene();
        this.initComposer();
        this.animate();
    }

    initScene() {
        // 创建场景、相机、渲染器等基础Three.js设置
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(this.renderer.domElement);

        // 添加一些测试物体
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
        this.cube = new THREE.Mesh(geometry, material);
        this.scene.add(this.cube);

        // 添加灯光
        const light = new THREE.DirectionalLight(0xffffff, 1);
        light.position.set(1, 1, 1);
        this.scene.add(light);

        this.camera.position.z = 5;
    }

    initComposer() {
        // 创建 EffectComposer
        this.composer = new EffectComposer(this.renderer);

        // 1. 基础渲染通道 - 必须放在第一位
        const renderPass = new RenderPass(this.scene, this.camera);
        this.composer.addPass(renderPass);

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

        // 3. 添加胶片颗粒效果
        const filmPass = new FilmPass(
            0.35,   // 噪声强度
            false   // 是否灰度
        );
        this.composer.addPass(filmPass);

        // 4. 添加故障效果
        const glitchPass = new GlitchPass();
        glitchPass.goWild = false; // 控制是否随机故障
        this.composer.addPass(glitchPass);

        // 5. 添加输出通道(将结果渲染到屏幕)
        const outputPass = new ShaderPass(CopyShader);
        outputPass.renderToScreen = true; // 重要:最后一个通道需要设置这个
        this.composer.addPass(outputPass);

        // 设置像素比例,避免模糊
        this.composer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    }

    animate() {
        requestAnimationFrame(() => this.animate());

        // 更新动画
        this.cube.rotation.x += 0.01;
        this.cube.rotation.y += 0.01;

        // 使用 composer 渲染
        this.composer.render();
    }
}

// 启动应用
new PostProcessingDemo();

1.4. 通道类型详解

1.4.1. 基础通道

javascript

// RenderPass - 将场景渲染到渲染目标
const renderPass = new RenderPass(scene, camera);

// MaskPass - 用于遮罩操作
const maskPass = new MaskPass(scene, camera);

// ClearPass - 清除渲染目标
const clearPass = new ClearPass();

1.4.2. 效果通道

javascript

// 辉光效果
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
const bloomPass = new UnrealBloomPass(resolution, strength, radius, threshold);

// 胶片效果
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';
const filmPass = new FilmPass(noiseIntensity, scanlineIntensity, scanlineCount, grayscale);

// 故障效果
import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';
const glitchPass = new GlitchPass();

1.4.3. 自定义着色器通道

javascript

// 创建自定义着色器
const CustomShader = {
    uniforms: {
        "tDiffuse": { value: null },
        "uTime": { value: 0.0 },
        "uStrength": { value: 0.5 }
    },
    vertexShader: `
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `,
    fragmentShader: `
        uniform sampler2D tDiffuse;
        uniform float uTime;
        uniform float uStrength;
        varying vec2 vUv;

        void main() {
            vec2 uv = vUv;
            // 添加波纹效果
            uv.x += sin(uv.y * 10.0 + uTime) * 0.01 * uStrength;
            vec4 color = texture2D(tDiffuse, uv);
            gl_FragColor = color;
        }
    `
};

// 创建自定义着色器通道
const customPass = new ShaderPass(CustomShader);
customPass.uniforms.uStrength.value = 0.3;

1.5. 高级工作流程技巧

1.5.1. 性能优化

javascript

// 降低分辨率提升性能
const composer = new EffectComposer(renderer);
composer.setSize(window.innerWidth / 2, window.innerHeight / 2);

// 选择性启用效果
bloomPass.enabled = true; // 或 false 来临时禁用

// 使用多采样抗锯齿
const renderer = new THREE.WebGLRenderer({ 
    antialias: true,
    powerPreference: "high-performance" 
});

1.5.2. 动态效果控制

javascript

class EffectController {
    constructor(composer) {
        this.composer = composer;
        this.effects = {};
        this.initGUI();
    }

    addEffect(name, pass) {
        this.effects[name] = pass;
    }

    initGUI() {
        // 使用 dat.GUI 或 lil-gui 创建控制面板
        const gui = new GUI();

        // 控制辉光效果
        const bloomFolder = gui.addFolder('Bloom');
        bloomFolder.add(bloomPass, 'enabled').name('Enabled');
        bloomFolder.add(bloomPass, 'strength', 0, 3).name('Strength');
        bloomFolder.add(bloomPass, 'radius', 0, 1).name('Radius');
        bloomFolder.add(bloomPass, 'threshold', 0, 1).name('Threshold');
        bloomFolder.open();

        // 控制胶片效果
        const filmFolder = gui.addFolder('Film');
        filmFolder.add(filmPass, 'enabled').name('Enabled');
        filmFolder.add(filmPass.uniforms.nIntensity, 'value', 0, 1).name('Noise');
        filmFolder.open();
    }
}

1.5.3. 渲染到纹理

javascript

// 创建额外的渲染目标用于特殊效果
const rtTexture = new THREE.WebGLRenderTarget(
    window.innerWidth, 
    window.innerHeight,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.LinearFilter,
        format: THREE.RGBAFormat
    }
);

// 在自定义着色器中使用
const customPass = new ShaderPass(CustomShader);
customPass.uniforms.tDiffuse.value = rtTexture.texture;

1.6. 常见问题与解决方案

1.6.1. 效果顺序问题

javascript

// 正确的通道顺序
composer.addPass(renderPass);      // 1. 基础渲染
composer.addPass(bloomPass);       // 2. 辉光效果
composer.addPass(colorCorrection); // 3. 颜色校正
composer.addPass(outputPass);      // 4. 输出(必须设置 renderToScreen = true)

1.6.2. 性能问题

1.6.3. 内存泄漏

javascript

// 清理资源
function dispose() {
    composer.dispose();
    renderTarget.dispose();
    // 清理所有通道
    passes.forEach(pass => {
        if (pass.dispose) pass.dispose();
    });
}

1.7. 实际应用示例

1.7.1. 游戏特效

javascript

// 角色受伤时的红色闪屏效果
const hurtEffect = new ShaderPass({
    uniforms: {
        tDiffuse: { value: null },
        uHurt: { value: 0.0 }
    },
    vertexShader: `...`,
    fragmentShader: `
        uniform sampler2D tDiffuse;
        uniform float uHurt;
        varying vec2 vUv;

        void main() {
            vec4 color = texture2D(tDiffuse, vUv);
            // 添加红色叠加
            color.rgb = mix(color.rgb, vec3(1.0, 0.0, 0.0), uHurt * 0.5);
            gl_FragColor = color;
        }
    `
});

// 触发受伤效果
function triggerHurtEffect() {
    hurtEffect.uniforms.uHurt.value = 1.0;
    // 渐隐效果
    const fadeOut = setInterval(() => {
        hurtEffect.uniforms.uHurt.value -= 0.1;
        if (hurtEffect.uniforms.uHurt.value <= 0) {
            clearInterval(fadeOut);
        }
    }, 100);
}

1.7.2. 艺术风格化渲染

javascript

// 卡通渲染效果组合
composer.addPass(renderPass);
composer.addPass(outlinePass);     // 边缘描边
composer.addPass(toonShaderPass);  // 卡通着色
composer.addPass(halftonePass);    // 半色调效果
composer.addPass(outputPass);

1.8. 总结

EffectComposer 的工作流程可以概括为:

  1. 初始化 - 创建渲染器、场景、相机

  2. 创建 Composer - 基于渲染器创建 EffectComposer

  3. 添加通道 - 按顺序添加各种效果通道

  4. 设置输出 - 确保最后一个通道设置 renderToScreen = true

  5. 渲染循环 - 使用 composer.render() 替代 renderer.render()

掌握 EffectComposer 可以让你为 Three.js 应用添加丰富的视觉效果,从简单的颜色调整到复杂的多重特效组合,极大提升视觉体验。