✨Three.js 实现炫酷辉光效果系统!附完整封装代码🔥

✨Three.js 实现炫酷辉光效果系统!附完整封装代码🔥

在做 WebGL 可视化项目时,你是否曾羡慕过那些发着柔光的按钮、泛着光晕的模型?✨ 今天就带大家用 Three.js 实现一个完全可控的辉光系统:支持 辉光强度调节自定义颜色局部开启/关闭独立控制每个物体的辉光


🔧 基础原理:辉光是怎么实现的?

Three.js 本身不支持辉光,但我们可以使用 后处理(postprocessing) 来模拟辉光效果:

  1. 利用 UnrealBloomPass 实现类似 HDR 的辉光通道。
  2. 使用 RenderPass 分别渲染出正常场景和辉光通道。
  3. 混合两者,形成最终炫酷视觉。

🛠️ 自定义辉光封装类 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()
  }
}
相关推荐
Mintopia1 天前
Three.js WebGPU 支持:利用 WebGPU 提升渲染性能
前端·javascript·three.js
爱看书的小沐2 天前
【小沐杂货铺】基于Three.JS构建IFC模型浏览器(WebGL、CAD、Revit、IFC)
javascript·webgl·three.js·bim·ifc·revit·ifc.js
答案answer2 天前
Three.js实现低代码开发的两种模式
前端·低代码·three.js
爱看书的小沐2 天前
【小沐杂货铺】基于Three.JS绘制太阳系Solar System(GIS 、WebGL、vue、react,提供全部源代码)第2期
javascript·vue.js·gis·webgl·three.js·地球·earth
Mintopia2 天前
Three.js 实时数据集成:让 3D 世界跳起舞来
前端·javascript·three.js
Mintopia3 天前
Three.js 后处理效果:给你的 3D 世界加一层 “魔法滤镜”
前端·javascript·three.js
Mintopia4 天前
Three.js 环境贴图:给你的 3D 世界加个梦幻滤镜
前端·javascript·three.js
Mintopia5 天前
Three.js 自定义着色器(Custom Shaders)
前端·javascript·three.js
Mintopia5 天前
Three.js 物理材质:打造 3D 世界的 “魔法皮肤”
前端·javascript·three.js