✨Three.js 实现炫酷辉光效果系统!附完整封装代码🔥
在做 WebGL 可视化项目时,你是否曾羡慕过那些发着柔光的按钮、泛着光晕的模型?✨ 今天就带大家用 Three.js 实现一个完全可控的辉光系统:支持 辉光强度调节 、自定义颜色 、局部开启/关闭 、独立控制每个物体的辉光!
🔧 基础原理:辉光是怎么实现的?
Three.js 本身不支持辉光,但我们可以使用 后处理(postprocessing) 来模拟辉光效果:
- 利用
UnrealBloomPass
实现类似 HDR 的辉光通道。 - 使用 RenderPass 分别渲染出正常场景和辉光通道。
- 混合两者,形成最终炫酷视觉。
🛠️ 自定义辉光封装类 CustomBloom
下面是我们封装的核心类 CustomBloom
功能:
✅ 支持功能:
- 🌈 自定义辉光颜色(默认白光,也可设为紫、蓝、绿......)
- 🎛️ 动态设置辉光参数(阈值、强度、半径、曝光度)
- 🔥 任意物体启用/关闭辉光(如选中高亮)
- ♻️ 支持动态切换开启/关闭系统
- 📏 支持窗口自适应尺寸调整
🚀 示例效果图
🎨 你可以实现这样的效果:
- 某个物体发出幽幽蓝光💡
- 选中时物体变红光🌟
- 其余场景自动变暗聚焦发光物体🔦
如图:
(图片来自官方示例)
🧩 如何使用 CustomBloom
?
js
// 1️⃣ 初始化
const bloom = new CustomBloom(scene, camera, renderer)
bloom.setBloomParameters({
threshold: 0.1,
strength: 1.5,
radius: 0.4,
exposure: 1,
bloomColor: '#ff00ff', // 紫色辉光
})
// 2️⃣ 控制某个物体开启辉光
bloom.addToBloomLayer(myMesh)
// 3️⃣ 渲染循环中调用
function animate() {
requestAnimationFrame(animate)
bloom.render()
}
✨ Bonus:支持单个物体自定义辉光颜色!
js
// 设置某个物体为绿色辉光
bloom.setObjectBloomColor(myMesh, '#00ff00')
// 重置物体为原始颜色
bloom.resetObjectBloomColor(myMesh)
🔁 启用/禁用辉光系统
js
// 禁用辉光(性能优化用)
bloom.disable()
// 重新启用辉光
bloom.enable()
✅ 使用建议 & Tips
💡 性能优化建议:
- 只对需要的物体启用辉光层。
- 在低性能设备中提供开启/关闭开关。
- 自定义着色器混合时避免重复采样。
💡 实战场景:
- 智慧城市中的设备告警状态高亮。
- 游戏中物品拾取提示。
- 可视化场景中的关键部件聚焦。
🏁 总结
使用 Three.js 的后处理系统,我们可以轻松实现类似真实世界中的辉光效果,并通过封装类 CustomBloom
实现高度可控、灵活配置的效果系统。
🔥 还等什么?把你的 Three.js 项目升级成视觉盛宴吧!
js
/**
* CustomBloom 类 - Three.js 的辉光效果管理器
* 提供了一个完整的辉光效果系统,支持:
* - 可配置的辉光参数(强度、阈值、半径等)
* - 自定义辉光颜色
* - 独立控制物体的辉光效果
* - 动态启用/禁用辉光系统
*/
// 导入必要的 Three.js 核心和后期处理模块
import * as THREE from 'three'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'
/**
* 默认的辉光效果参数配置
* @const {Object} DEFAULT_BLOOM_OPTIONS
* @property {number} threshold - 辉光效果的亮度阈值,只有超过此亮度的像素才会发光
* @property {number} strength - 辉光效果的强度
* @property {number} radius - 辉光效果的扩散半径
* @property {number} exposure - 整体场景的曝光度
* @property {THREE.Color} bloomColor - 辉光效果的颜色,默认为白色
*/
const DEFAULT_BLOOM_OPTIONS = {
threshold: 0,
strength: 1,
radius: 0.5,
exposure: 1,
bloomColor: new THREE.Color(1, 1, 1), // 添加默认的辉光颜色(白色)
}
/**
* 自定义着色器定义
* 用于实现辉光效果的颜色混合
* @const {Object} BLOOM_LAYER_SHADER
* @property {Object} uniforms - 着色器统一变量
* @property {string} vertexShader - 顶点着色器代码
* @property {string} fragmentShader - 片段着色器代码
*/
const BLOOM_LAYER_SHADER = {
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: null },
bloomColor: { value: new THREE.Color(1, 1, 1) }, // 添加颜色uniform
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
uniform vec3 bloomColor;
varying vec2 vUv;
void main() {
vec4 bloomColor = vec4(bloomColor, 1.0) * texture2D(bloomTexture, vUv);
gl_FragColor = texture2D(baseTexture, vUv) + bloomColor;
}
`,
}
export default class CustomBloom {
/**
* 创建一个新的辉光效果实例
* @constructor
* @param {THREE.Scene} scene - Three.js 场景实例
* @param {THREE.Camera} camera - Three.js 相机实例
* @param {THREE.WebGLRenderer} renderer - Three.js 渲染器实例
*/
constructor(scene, camera, renderer) {
this._scene = scene
this._camera = camera
this._renderer = renderer
this._bloomLayer = new THREE.Layers()
this._BLOOM_SCENE = 1
this._darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' })
this._materials = {}
this._bloomColor = new THREE.Color(1, 1, 1)
this._initBloomEffect()
}
/**
* 初始化辉光效果的各个组件
* 包括:渲染通道、辉光通道、混合通道等
* @private
*/
_initBloomEffect() {
this._bloomLayer.set(this._BLOOM_SCENE)
const renderScene = new RenderPass(this._scene, this._camera)
this._bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85,
)
this._bloomComposer = new EffectComposer(this._renderer)
this._bloomComposer.renderToScreen = false
this._bloomComposer.addPass(renderScene)
this._bloomComposer.addPass(this._bloomPass)
// 修改混合通道以支持颜色
this._mixPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: this._bloomComposer.renderTarget2.texture },
bloomColor: { value: this._bloomColor }, // 添加颜色uniform
},
vertexShader: BLOOM_LAYER_SHADER.vertexShader,
fragmentShader: BLOOM_LAYER_SHADER.fragmentShader,
}),
'baseTexture',
)
this._mixPass.needsSwap = true
this._finalComposer = new EffectComposer(this._renderer)
this._finalComposer.addPass(renderScene)
this._finalComposer.addPass(this._mixPass)
this._finalComposer.addPass(new OutputPass())
}
/**
* 更新辉光效果的参数配置
* @param {Object} options - 辉光参数配置对象
* @param {number} [options.threshold] - 辉光阈值
* @param {number} [options.strength] - 辉光强度
* @param {number} [options.radius] - 辉光半径
* @param {number} [options.exposure] - 场景曝光度
* @param {THREE.Color|string|number} [options.bloomColor] - 辉光颜色
*/
setBloomParameters(options = {}) {
const params = { ...DEFAULT_BLOOM_OPTIONS, ...options }
this._bloomPass.threshold = params.threshold
this._bloomPass.strength = params.strength
this._bloomPass.radius = params.radius
this._renderer.toneMappingExposure = Math.pow(params.exposure, 4.0)
// 设置辉光颜色
if (params.bloomColor) {
this.setBloomColor(params.bloomColor)
}
}
/**
* 设置辉光效果的整体颜色
* @param {THREE.Color|string|number} color - 新的辉光颜色值
*/
setBloomColor(color) {
this._bloomColor = new THREE.Color(color)
this._mixPass.material.uniforms.bloomColor.value = this._bloomColor
}
/**
* 为特定物体设置独立的辉光颜色,会保存物体的原始颜色
* @param {THREE.Object3D} object - 目标物体
* @param {THREE.Color|string|number} color - 辉光颜色值
*/
setObjectBloomColor(object, color) {
if (object.material) {
// 保存原始颜色
if (!object.userData.originalColor) {
object.userData.originalColor = object.material.color.clone()
}
// 设置新颜色
object.material.color = new THREE.Color(color)
}
}
/**
* 将物体的辉光颜色重置为原始颜色
* @param {THREE.Object3D} object - 要重置的物体
*/
resetObjectBloomColor(object) {
if (object.material && object.userData.originalColor) {
object.material.color = object.userData.originalColor
delete object.userData.originalColor
}
}
/**
* 将指定物体添加到辉光渲染层
* @param {THREE.Object3D} object - 要添加辉光效果的物体
*/
addToBloomLayer(object) {
object.layers.enable(this._BLOOM_SCENE)
}
/**
* 从辉光渲染层移除指定物体
* @param {THREE.Object3D} object - 要移除辉光效果的物体
*/
removeFromBloomLayer(object) {
object.layers.disable(this._BLOOM_SCENE)
}
/**
* 切换物体的辉光效果状态
* @param {THREE.Object3D} object - 要切换辉光状态的物体
*/
toggleBloom(object) {
object.layers.toggle(this._BLOOM_SCENE)
}
/**
* 遍历场景,将非辉光层的物体材质变暗
* @private
*/
_darkenNonBloomed() {
const self = this
this._scene.traverse(function (obj) {
if (obj.isMesh && !self._bloomLayer.test(obj.layers)) {
self._materials[obj.uuid] = obj.material
obj.material = self._darkMaterial
}
})
}
/**
* 恢复场景中所有物体的原始材质
* @private
*/
_restoreMaterial() {
const self = this
this._scene.traverse(function (obj) {
if (self._materials[obj.uuid]) {
obj.material = self._materials[obj.uuid]
delete self._materials[obj.uuid]
}
})
}
/**
* 更新辉光效果的渲染尺寸
* @param {number} width - 新的渲染宽度
* @param {number} height - 新的渲染高度
*/
setSize(width, height) {
this._bloomComposer.setSize(width, height)
this._finalComposer.setSize(width, height)
}
/**
* 销毁辉光效果,释放资源
* 清理所有相关的材质、通道和合成器
*/
dispose() {
// 清理所有物体的辉光层
this._scene.traverse((obj) => {
if (obj.layers) {
obj.layers.disable(this._BLOOM_SCENE)
}
})
// 恢复所有物体的原始材质
this._restoreMaterial()
// 销毁材质
this._darkMaterial.dispose()
// 销毁渲染通道
this._bloomPass.dispose()
this._mixPass.dispose()
// 销毁合成器
this._bloomComposer.dispose()
this._finalComposer.dispose()
// 清理引用
this._bloomPass = null
this._mixPass = null
this._bloomComposer = null
this._finalComposer = null
this._materials = {}
}
/**
* 临时禁用辉光效果,但保留配置
*/
disable() {
this._enabled = false
// 恢复所有物体的原始材质
this._restoreMaterial()
}
/**
* 重新启用之前禁用的辉光效果
*/
enable() {
this._enabled = true
}
/**
* 执行辉光效果的渲染
* 包括辉光层的渲染和最终图像的合成
*/
render() {
if (!this._enabled) {
// 如果禁用了辉光效果,直接使用普通渲染
this._renderer.render(this._scene, this._camera)
return
}
// 渲染辉光部分
this._darkenNonBloomed()
this._bloomComposer.render()
this._restoreMaterial()
// 渲染最终效果
this._finalComposer.render()
}
}