场景内使用
//创建后期通道this.effectManager = new EffectManager({ renderer, camera, scene, dom })//循环渲染// 动画----------effect为我控制后期特效的开关animate() {requestAnimationFrame(this.animate);let { camera, controls, effectManager, effect } = thisif (!camera) returnif (!controls) returnlet { scene, renderer, renderer_3d, stats, } = this.glcontrols.update();renderer_3d.render(scene, camera);// effect 执行renderif (effect.active) {effectManager.render()} else {renderer.render(scene, camera);}stats.update();}animate()//执行动画//使用loadConfig方法修改辉光通道let blooms = [{BLOOM_LEVEL: "1",threshold: 0,strength: 1,radius: 0.1,exposure: .1},{BLOOM_LEVEL: "2",threshold: 0,strength: .1,radius: 0.7,exposure: .9},]this.effectManager.loadConfig(blooms )// 使特定的物体发光//找到mesh,假设已经有一个bloom_mesh(THREE.mesh) bloom_mesh.BLOOM_LEVEL="2" //使用 BLOOM_LEVEL: "2",渲染,场景发光
/**
do sth........................
later......
*/bloom_mesh.BLOOM_LEVEL="1" //使用 BLOOM_LEVEL: "1",渲染,可以随时修改
创建后期处理通道的类
import * as THREE from 'three'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
import BloomEffect from './BloomEffect';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js';
export default class EffectManager {constructor({ renderer, camera, scene, dom }) {// 当有多个类型的后期处理时,通过这里修改配置项this.effects = {blooms: [{BLOOM_LEVEL: "1",threshold: 0,strength: 1,radius: 0.1,exposure: .1},{BLOOM_LEVEL: "2",threshold: 0,strength: .1,radius: 0.7,exposure: .9},]}this.renderer = rendererthis.camera = camerathis.scene = scenethis.dom = domthis.blooms = []this.materials = {}this.bloomHandleList = {}this.darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });this.sceneBgCache = nullthis.createDefuaultPass()this.upDate()}// 一些默认的通道createDefuaultPass() {let { renderer, camera, scene, dom } = thisconst renderScene = new RenderPass(scene, camera);const outputPass = new OutputPass();const effectFXAAfix = new ShaderPass(FXAAShader);this.renderScene = renderScenethis.effectFXAAfix = effectFXAAfixthis.outputPass = outputPass}// 渲染通道:bloomupDateBlooms() {let list = this.effects.bloomslet { renderer, camera, scene, renderScene, dom } = thisthis.blooms = list.map(_c => new BloomEffect(renderer, camera, scene, renderScene, dom,{threshold: _c.threshold,strength: _c.strength,radius: _c.radius,exposure: _c.exposure},_c.BLOOM_LEVEL))}// 更新通道,upDate() {let { renderScene, effectFXAAfix, outputPass, renderer, camera, dom, scene } = thisconst finalComposer = new EffectComposer(renderer);finalComposer.addPass(renderScene);this.upDateBlooms()this.blooms.forEach(bloom => {finalComposer.addPass(bloom.mixPass)})// finalComposer.addPass(smaaPass)finalComposer.addPass(effectFXAAfix)finalComposer.addPass(outputPass);this.finalComposer = finalComposer}darkenNonBloomed() {let { scene, materials, bloomHandleList, darkMaterial } = thisscene.traverse((obj) => {if (obj.material) {materials[obj.uuid] = obj.material;obj.material = darkMaterial;}if (obj.BLOOM_LEVEL) {if (bloomHandleList[obj.BLOOM_LEVEL]) {bloomHandleList[obj.BLOOM_LEVEL].push(obj)} else {bloomHandleList[obj.BLOOM_LEVEL] = [obj]}}})this.sceneBgCache = scene.backgroundscene.background = darkMaterial}restoreMaterial() {let { materials, scene } = thisscene.traverse((obj) => {if (materials[obj.uuid]) {obj.material = materials[obj.uuid];delete materials[obj.uuid];}})scene.background = this.sceneBgCache}render() {let {materials, bloomHandleList,finalComposer,blooms} = thisthis.darkenNonBloomed()blooms.forEach(bloom => {bloom.render(materials, bloomHandleList)})this.restoreMaterial()finalComposer.render();}setCamera(_camera) {this.camera = _camerathis.renderScene.camera = this.camera}// 通道和composer无法修改分辨率只能用updateresize(x, y) {this.upDate()this.finalComposer.setSize(x, y)}// 改变blooms 可以是别的类型的特效 这里以辉光为例loadConfig(data) {console.log('改变blooms', data);let { blooms } = dataif (blooms) {this.effects.blooms = blooms// this.u}this.upDate()}// reRenderEffect重置}
后期类内部的辉光通道类
import * as THREE from 'three'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';export default class BloomEffect {/*** * @param {*} param0 */constructor(renderer,camera,scene,renderScene,dom,params = {threshold: 0,strength: 1,radius: 0.5,exposure: 1},BLOOM_LEVEL = 0,) {this.renderer = rendererthis.camera = camerathis.scene = scenethis.BLOOM_LEVEL = BLOOM_LEVELconst BLOOM_SCENE = BLOOM_LEVEL;const bloomLayer = new THREE.Layers();bloomLayer.set(BLOOM_SCENE);const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });const materials = {};// bloomconst bloomPass = new UnrealBloomPass(new THREE.Vector2(1, 1), 1.5, 0.4, 0.85);bloomPass.threshold = params.threshold;bloomPass.strength = params.strength;bloomPass.radius = params.radius;const bloomComposer = new EffectComposer(renderer);bloomComposer.renderToScreen = false;bloomComposer.addPass(renderScene);bloomComposer.addPass(bloomPass);// mixconst mixPass = new ShaderPass(new THREE.ShaderMaterial({uniforms: {baseTexture: { value: null },bloomTexture: { value: bloomComposer.renderTarget2.texture }},vertexShader: /*glsl*/ `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}`,fragmentShader: /*glsl*/`uniform sampler2D baseTexture;uniform sampler2D bloomTexture;varying vec2 vUv;void main() {gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ,1.0,1.0,.0) * texture2D( bloomTexture, vUv ) );}`,defines: {}}), 'baseTexture');mixPass.needsSwap = true;this.BLOOM_SCENE = BLOOM_SCENEthis.bloomLayer = bloomLayerthis.params = paramsthis.darkMaterial = darkMaterialthis.materials = materialsthis.renderScene = renderScenethis.bloomPass = bloomPassthis.bloomComposer = bloomComposerthis.mixPass = mixPassfunction darkenNonBloomed(obj) {if (obj.material && (!obj.BLOOM_LEVEL)) {materials[obj.uuid] = obj.material;obj.material = darkMaterial;}// if (obj.BLOOM_LEVEL && (obj.BLOOM_LEVEL !== BLOOM_LEVEL)) {// materials[obj.uuid] = obj.material;// obj.material = darkMaterial;// }}this.darkenNonBloomed = darkenNonBloomedfunction restoreMaterial(obj) {if (materials[obj.uuid]) {obj.material = materials[obj.uuid];delete materials[obj.uuid];}}this.restoreMaterial = restoreMaterial}render(materials, bloomHandleList) {// let { scene, bloomComposer,// darkenNonBloomed, restoreMaterial,// } = this// scene.traverse(darkenNonBloomed);// bloomComposer.render();// scene.traverse(restoreMaterial);let {BLOOM_LEVEL,scene, bloomComposer} = thislet list = nullif (bloomHandleList[BLOOM_LEVEL]) {list = bloomHandleList[BLOOM_LEVEL]list.forEach(obj => {obj.material = materials[obj.uuid]});}bloomComposer.render();if (list) {list.forEach(obj => {materials[obj.uuid] = obj.materialobj.material = this.darkMaterial});delete bloomHandleList[BLOOM_LEVEL]}}setParams(_params = {}) {let { params, bloomPass } = thisfor (let k in params) {if (_params[k]) {params[k] = params[k]}}Object.assign(bloomPass, params)}setCamera(_camera) {let { renderScene } = thisrenderScene.setCamera(_camera)}}
效果
我在花朵和瓶子上分别用了1 和2 辉光效果,下图是无光效果对比