在React+@react-three/fiber中实现仅针对太阳的选择性Bloom效果,不影响场景背景及其他物体
在React+@react-three/fiber中实现仅针对太阳的选择性Bloom效果,不影响场景背景及其他物体
我之前也碰到过几乎一模一样的问题!折腾了好几天才搞明白核心问题出在哪,给你分享下我是怎么解决的:
核心思路
选择性Bloom的关键是把需要发光的物体(太阳)放到单独的图层,让Bloom效果只处理这个图层的内容,再把发光效果叠加到完整的主场景上。你之前的尝试方向是对的,但图层设置和后处理的结构没搭配好,导致要么全场景发光要么其他物体消失。
分步解决方案
1. 统一管理图层常量
先定义一个专用的Bloom图层,避免硬编码数字出错:
import * as THREE from 'three'; // 创建一个仅用于Bloom的图层 const BLOOM_LAYER = new THREE.Layers(); BLOOM_LAYER.set(1); // 用第1层,默认物体都在第0层
2. 修改太阳组件:标记为Bloom目标
把太阳放到专用图层,并用Selection组件标记它是后处理的目标物体,不要用Select直接包裹(那是给后处理效果用的):
function Sun() { const sunTexture = useTexture("/textures/2k_sun.jpg"); sunTexture.colorSpace = THREE.SRGBColorSpace; const sunRef = useRef<THREE.Mesh>(null); useFrame((_, delta) => { sunRef.current?.rotation.y += 0.001; }); return ( <Selection> {/* 标记这个物体是后处理要选中的目标 */} <mesh ref={sunRef} scale={7} layers={BLOOM_LAYER}> <sphereGeometry args={[1, 32, 32]} /> {/* 关闭色调映射,避免太阳的亮部被压暗 */} <meshBasicMaterial map={sunTexture} toneMapped={false} /> </mesh> </Selection> ); }
3. 正确配置后处理流程
这是最关键的一步!要让后处理先渲染完整的主场景,再单独渲染太阳并应用Bloom,最后把发光效果叠加到主场景上:
import { EffectComposer, RenderPass, Select, BloomEffect } from '@react-three/postprocessing'; import { useThree } from '@react-three/fiber'; export default function SolarSystem() { const { gl, scene, camera } = useThree(); return ( <> {/* 先渲染所有场景元素:背景、太阳、行星 */} <Background /> <Sun /> {/* 这里放你的行星组件,比如<Planet name="Mercury" ... /> */} {/* 后处理核心流程 */} <EffectComposer gl={gl}> {/* 第一步:渲染完整的主场景(所有图层,包括背景、行星、太阳) */} <RenderPass scene={scene} camera={camera} /> {/* 第二步:只渲染选中的太阳(BLOOM_LAYER),应用Bloom后叠加到主场景 */} <Select enabled> <BloomEffect intensity={2.5} // 发光强度,按需调整 threshold={0.2} // 过滤暗部,避免微弱反光也被发光 blurPasses={4} // 模糊程度,值越高越柔和 /> </Select> </EffectComposer> </> ); }
4. 修复背景ShaderMaterial的渲染问题
确保背景不在Bloom图层上,并且设置正确的渲染属性,避免被后处理干扰:
function Background() { const meshRef = useRef<THREE.Mesh>(null); const { camera } = useThree(); const material = useMemo( () => new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { iTime: { value: 0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, }, side: THREE.BackSide, depthWrite: false, // 不写入深度缓冲,避免遮挡其他物体 toneMapped: false, // 星空背景不需要色调映射 }), [] ); useFrame((state) => { material.uniforms.iTime.value = state.clock.getElapsedTime(); meshRef.current?.position.copy(camera.position); }); // 显式设置背景在默认图层,确保不会被Bloom处理 return ( <mesh ref={meshRef} renderOrder={-1} layers={new THREE.Layers().set(0)}> <sphereGeometry args={[1000, 32, 32]} /> <primitive object={material} /> </mesh> ); }
额外注意事项
- 行星和其他物体默认在第0层,不需要额外设置,自然不会被Bloom处理
- 调整
BloomEffect的参数可以优化发光效果:threshold越高,只有太阳最亮的部分会发光;intensity控制发光的强弱 - 太阳用
meshBasicMaterial是对的,因为它是自发光体,不需要受场景光照影响,关掉toneMapped能保证它的亮度不会被引擎自动压暗
我用这套方法解决了和你完全一样的问题,现在只有太阳有柔和的发光效果,背景和行星都完全不受影响,性能也保持得很好。你可以根据自己的视觉需求调整Bloom的参数,直到达到满意的效果!
内容来源于stack exchange
相关产品推荐
相关产品推荐

