EffectComposer是 Three.js 中用于后处理(Post-processing)的一个类。它允许你在渲染完成之后对图像进行进一步的处理,例如添加特效、模糊、深度效果、色调映射等,进而实现更加丰富的视觉效果。
EffectComposer相关的Pass是 Three.js 后期处理(Post-processing)系统的一部分,下面是一些常见的Pass,它们主要用于在渲染结果上应用各种效果。你可以通过组合这些Pass来实现复杂的后期处理效果。
EffectComposer 有五个属性,十个方法
EffectComposer( renderer : WebGLRenderer, renderTarget : WebGLRenderTarget )
renderer -- 用于渲染场景的渲染器。
renderTarget -- (可选)一个预先配置的渲染目标,内部由 EffectComposer 使用。
<template>
<div id="parkingLot" ref="parkingLot">
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from 'three';
import TWEEN from 'three/addons/libs/tween.module.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { RenderTransitionPass } from 'three/addons/postprocessing/RenderTransitionPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
const parkingLot = ref();
onMounted(async () => {
const DOMEl = parkingLot.value;
// 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
const width = DOMEl.clientWidth;
const height = DOMEl.clientHeight;
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio ); // 设置渲染器的像素比,适应设备屏幕的分辨率
renderer.setSize( width, height ); // 设置渲染器的大小
DOMEl.appendChild( renderer.domElement ); // 将渲染器的DOM元素添加到指定的容器中
const textures = []; // 存储纹理的数组
const clock = new THREE.Clock(); // 创建一个时钟,用于跟踪时间
const initTextures = async function () {
const loader = new THREE.TextureLoader(); // 创建纹理加载器
for ( let i = 0; i < 6; i++ ) {
console.log(i,"i")
// 加载6张过渡纹理
textures[i] = await loader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/textures/transition/transition' + ( i + 1 ) + '.png' );
}
}
initTextures();
// 用于控制过渡动画的参数
const params = {
transition: 0, // 当前过渡状态
texture: 5, // 当前纹理索引
cycle: true, // 是否循环切换纹理
threshold: 0.1, // 过渡阈值
sceneAnimate: true,
transitionAnimate: true,
useTexture: true
};
const gui = new GUI();
gui.add( params, 'sceneAnimate' ).name( 'Animate scene' ); // 设置属性名称
gui.add( params, 'transitionAnimate' ).name( 'Animate transition' );// 设置属名称
gui.add( params, 'transition', 0, 1, 0.01 ).onChange( function ( value ) { // 监听 过渡值变化事件
renderTransitionPass.setTransition( value );
} ).listen();
gui.add( params, 'useTexture' ).onChange( function ( value ) { // 监听使用纹理事件
renderTransitionPass.useTexture( value );
} );
gui.add( params, 'texture', { Perlin: 0, Squares: 1, Cells: 2, Distort: 3, Gradient: 4, Radial: 5 } ).onChange( function ( value ) { // 监听纹理变化事件
renderTransitionPass.setTexture( textures[ value ] );
} ).listen();
gui.add( params, 'cycle' ); // 添加
gui.add( params, 'threshold', 0, 1, 0.01 ).onChange( function ( value ) {
renderTransitionPass.setTextureThreshold( value );
} );
// 创建两个FX场景,用于过渡效果的显示
const fxSceneA = new FXScene( new THREE.BoxGeometry( 2, 2, 2 ), new THREE.Vector3( 0, - 0.4, 0 ), 0xffffff );
const fxSceneB = new FXScene( new THREE.IcosahedronGeometry( 1, 1 ), new THREE.Vector3( 0, 0.2, 0.1 ), 0x000000 );
// 设置动画循环
renderer.setAnimationLoop( animate );
// 创建一个后处理合成器
const composer = new EffectComposer( renderer );
// 创建渲染过渡效果的Pass
const renderTransitionPass = new RenderTransitionPass( fxSceneA.scene, fxSceneA.camera, fxSceneB.scene, fxSceneB.camera );
renderTransitionPass.setTexture( textures[ 0 ] ); // 设置初始纹理
composer.addPass( renderTransitionPass );
// 创建输出Pass
const outputPass = new OutputPass();
composer.addPass( outputPass );
// 设置过渡动画
new TWEEN.Tween( params )
.to( { transition: 1 }, 1500 ) // 设置过渡动画的目标值和持续时间
.onUpdate( function () {
renderTransitionPass.setTransition( params.transition ); // 更新过渡状态
// 每次过渡后切换当前的纹理
if ( params.cycle ) {
if ( params.transition == 0 || params.transition == 1 ) {
// 切换到下一个纹理
params.texture = ( params.texture + 1 ) % textures.length;
renderTransitionPass.setTexture( textures[ params.texture ] );
}
}
} )
.repeat( Infinity ) // 无限循环
.delay( 2000 ) // 延迟开始
.yoyo( true ) // 启用往返动画效果
.start();
function animate() {
const delta = clock.getDelta(); // 获取每帧的时间间隔
fxSceneA.update( delta ); // 更新场景A
fxSceneB.update( delta ); // 更新场景B
render(); // 渲染当前场景
}
function render() {
console.log(params,"params")
// 根据过渡状态渲染场景
if ( params.transition === 0 ) {
renderer.render( fxSceneB.scene, fxSceneB.camera ); // 渲染场景B
} else if ( params.transition === 1 ) {
renderer.render( fxSceneA.scene, fxSceneA.camera ); // 渲染场景A
} else {
// 当过渡值在 0 到 1 之间时,渲染过渡效果
composer.render();
}
}
// 定义FX场景构造函数
function FXScene( geometry, rotationSpeed, backgroundColor ) {
const camera = new THREE.PerspectiveCamera( 50, width / height, 0.1, 100 ); // 创建透视相机
camera.position.z = 20; // 设置相机位置
// 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color( backgroundColor ); // 设置背景颜色
scene.add( new THREE.AmbientLight( 0xaaaaaa, 3 ) ); // 添加环境光
// 创建一个定向光源
const light = new THREE.DirectionalLight( 0xffffff, 3 );
light.position.set( 0, 1, 4 );
scene.add( light );
this.rotationSpeed = rotationSpeed;
// 根据几何体类型选择颜色
const color = geometry.type === 'BoxGeometry' ? 0x0000ff : 0xff0000;
const material = new THREE.MeshPhongMaterial( { color: color, flatShading: true } ); // 创建Phong材质
const mesh = generateInstancedMesh( geometry, material, 500 ); // 创建实例化网格
scene.add( mesh );
this.scene = scene;
this.camera = camera;
this.mesh = mesh;
// 更新场景中物体的旋转
this.update = function ( delta ) {
mesh.rotation.x += this.rotationSpeed.x * delta;
mesh.rotation.y += this.rotationSpeed.y * delta;
mesh.rotation.z += this.rotationSpeed.z * delta;
};
// 移除了resize处理
this.resize = function () {
// 如果需要处理resize事件,可以在这里添加
camera.aspect = width / height;
camera.updateProjectionMatrix();
};
}
// 创建实例化网格的函数
function generateInstancedMesh( geometry, material, count ) {
const mesh = new THREE.InstancedMesh( geometry, material, count ); // 创建实例化网格
const dummy = new THREE.Object3D(); // 创建一个空的Object3D对象,用于设置每个实例的位置、旋转和缩放
const color = new THREE.Color();
for ( let i = 0; i < count; i++ ) {
dummy.position.x = Math.random() * 100 - 50; // 随机位置
dummy.position.y = Math.random() * 60 - 30;
dummy.position.z = Math.random() * 80 - 40;
dummy.rotation.x = Math.random() * 2 * Math.PI; // 随机旋转
dummy.rotation.y = Math.random() * 2 * Math.PI;
dummy.rotation.z = Math.random() * 2 * Math.PI;
dummy.scale.x = Math.random() * 2 + 1; // 随机缩放
if ( geometry.type === 'BoxGeometry' ) {
dummy.scale.y = Math.random() * 2 + 1;
dummy.scale.z = Math.random() * 2 + 1;
} else {
dummy.scale.y = dummy.scale.x;
dummy.scale.z = dummy.scale.x;
}
dummy.updateMatrix(); // 更新矩阵
mesh.setMatrixAt( i, dummy.matrix ); // 设置实例的矩阵
mesh.setColorAt( i, color.setScalar( 0.1 + 0.9 * Math.random() ) ); // 设置每个实例的颜色
}
return mesh; // 返回生成的实例化网格
}
});
</script>
<style lang="scss" scoped="scoped">
#parkingLot {
width: 940px;
height: 940px;
border: 1px solid #ccc;
margin: 30px auto;
}
</style>
属性
- passes : Array 在代码中,
passes是EffectComposer类的一个属性,它是一个包含所有渲染过程的数组。每个渲染过程被封装为一个 Pass(通道),用于在渲染过程中执行一系列图像效果或后期处理操作。在EffectComposer中,addPass()用于将不同的 passes 加入到渲染管线,最终通过composer.render()渲染所有效果。 - readBuffer : WebGLRenderTarget 在 Three.js 中,
readBuffer是一个WebGLRenderTarget类型的对象,用于指定渲染的输入目标。它通常在后期处理或者渲染到纹理时使用,尤其是在使用EffectComposer和Pass时。
// 图像效果链:每个 pass 都可以读取上一个 pass 的渲染结果,进行处理后再输出到 writeBuffer,或者直接将结果呈现到屏幕。
// 后期处理:在一些特殊的后期效果中,比如模糊、颜色校正等,可以从 readBuffer 中获取当前帧的图像数据进行处理。
const composer = new EffectComposer( renderer );
// 创建渲染目标
const readBuffer = new THREE.WebGLRenderTarget( width, height );
// 用于后期效果的 pass
const renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );
// 自定义的 pass,需要读取和写入纹理
const customPass = new CustomPass();
customPass.readBuffer = readBuffer; // 指定读的渲染目标
composer.addPass( customPass );
// 渲染效果链
composer.render();
- renderer : WebGLRenderer 内部渲染器的引用。
- renderToScreen : Boolean 在 Three.js 中,renderToScreen 是一个布尔值(Boolean)属性,用于指定渲染结果是否直接显示到屏幕上。
// renderToScreen = true: 将渲染结果直接显示到屏幕上。这意味着,当前的渲染结果将被最终输出,通常在渲染管线的最后一个 pass 中使用。
// renderToScreen = false: 渲染结果不会直接显示到屏幕上。相反,渲染结果可能会被用于后续的图像处理或写入到纹理中,用于进一步的效果(比如模糊、颜色校正等)。
const composer = new EffectComposer( renderer );
// 渲染场景并应用后期效果
const renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );
// 自定义的后期效果 pass
const customPass = new CustomPass();
customPass.renderToScreen = true; // 指定将结果渲染到屏幕
composer.addPass( customPass );
// 渲染效果链
composer.render();
- writeBuffer : WebGLRenderTarget 在 Three.js 中,writeBuffer 是一个 WebGLRenderTarget 对象,通常用作渲染操作的输出目标。它用于存储从当前 pass 渲染产生的结果,通常是在后期处理链中使用,尤其是在 EffectComposer 和自定义 Pass 中。
// writeBuffer 指定了当前 pass 渲染结果的输出目标。它通常是一个 WebGLRenderTarget,表示一个渲染目标纹理或帧缓冲区。
// 渲染结果会写入到 writeBuffer 中,然后这个缓冲区的内容可以被用作后续 pass 的输入(通过 readBuffer)。
const composer = new EffectComposer( renderer );
// 创建渲染目标
const writeBuffer = new THREE.WebGLRenderTarget( width, height );
// 渲染场景并应用后期效果
const renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );
// 自定义的后期效果 pass
const customPass = new CustomPass();
customPass.writeBuffer = writeBuffer; // 指定渲染结果输出的目标
composer.addPass( customPass );
// 渲染效果链
composer.render();
方法
- addPass ( pass : Pass ) : undefined pass -- 将被添加到过程链的过程 将传入的过程添加到过程链。
- dispose () : undefined 销毁
- insertPass ( pass : Pass, index : Integer ) : undefined pass -- 将被插入到过程链的过程。 index -- 定义过程链中过程应插入的位置。 将传入的过程插入到过程链中所给定的索引处。
- isLastEnabledPass ( passIndex : Integer ) : Boolean 在 Three.js 中,
isLastEnabledPass(passIndex: Integer): Boolean方法用于判断指定索引的 pass 是否为渲染链中的最后一个启用的 pass。这个方法通常用于处理复杂的后期效果或渲染管线,特别是在你需要知道当前 pass 是否是最后一个渲染步骤时。
const composer = new THREE.EffectComposer(renderer);
// 定义一些 pass
const renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
const blurPass = new THREE.ShaderPass(blurShader);
composer.addPass(blurPass);
const finalPass = new THREE.ShaderPass(finalShader);
composer.addPass(finalPass);
// 检查 finalPass 是否是最后一个启用的 pass
if (composer.isLastEnabledPass(2)) {
console.log("finalPass 是最后一个启用的 pass。");
} else {
console.log("finalPass 不是最后一个启用的 pass。");
}
// 渲染场景
composer.render();
- removePass ( pass : Pass ) : undefined 与 addPass 相反
- render ( deltaTime : Float ) : undefined 执行所有启用的后期处理过程,来产生最终的帧,
- reset ( renderTarget : WebGLRenderTarget ) : undefined renderTarget -- (可选)一个预先配置的渲染目标,内部由 EffectComposer 使用。 重置所有EffectComposer的内部状态。
- setPixelRatio ( pixelRatio : Float ) : undefined pixelRatio -- 设备像素比 设置设备的像素比。该值通常被用于HiDPI设备,以阻止模糊的输出。 因此,该方法语义类似于WebGLRenderer.setPixelRatio()。
- setSize ( width : Integer, height : Integer ) : undefined width -- EffectComposer的宽度。 height -- EffectComposer的高度。 考虑设备像素比,重新设置内部渲染缓冲和过程的大小为(width, height)。 因此,该方法语义类似于WebGLRenderer.setSize()。
- swapBuffers () : undefined 交换内部的读/写缓冲。