Skip to content

Bloom — 辉光

Bloom(辉光/泛光)模拟真实相机和人眼的光学现象:极亮的光源会在周围产生光晕。霓虹灯、火焰、激光——加上 Bloom 后都会更有"发光"的质感。

启用 Bloom

Bloom 是一个组件,直接加到相机实体上。Bevy 提供了几个预设常量:

rust
use bevy::{
    core_pipeline::tonemapping::Tonemapping,
    post_process::bloom::Bloom,
    prelude::*,
    camera::Hdr,
};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, toggle_bloom)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    commands.spawn((
        Camera3d::default(),
        Hdr,
        Tonemapping::TonyMcMapface,
        Transform::from_xyz(0.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
        Bloom::NATURAL,
    ));

    commands.spawn((
        DirectionalLight {
            illuminance: 15000.0,
            ..default()
        },
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, -0.5, -0.5)),
    ));

    let emissive_strong = materials.add(StandardMaterial {
        emissive: LinearRgba::rgb(0.0, 0.0, 150.0),
        ..default()
    });
    let emissive_mild = materials.add(StandardMaterial {
        emissive: LinearRgba::rgb(10.0, 0.0, 0.0),
        ..default()
    });
    let dark = materials.add(Color::BLACK);
    let sphere = meshes.add(Sphere::new(0.4));

    for x in -3i32..3 {
        for z in -3i32..3 {
            let (mat, scale) = match (x + z).rem_euclid(3) {
                0 => (emissive_strong.clone(), 0.5),
                1 => (emissive_mild.clone(), 1.0),
                _ => (dark.clone(), 1.5),
            };
            commands.spawn((
                Mesh3d(sphere.clone()),
                MeshMaterial3d(mat),
                Transform::from_xyz(x as f32 * 1.5, 0.0, z as f32 * 1.5)
                    .with_scale(Vec3::splat(scale)),
            ));
        }
    }

    commands.spawn((
        Text::default(),
        Node {
            position_type: PositionType::Absolute,
            bottom: px(12),
            left: px(12),
            ..default()
        },
    ));
}

fn toggle_bloom(
    camera: Single<(Entity, Option<&Bloom>), With<Camera>>,
    mut text: Single<&mut Text>,
    mut commands: Commands,
    keys: Res<ButtonInput<KeyCode>>,
) {
    let (entity, bloom) = camera.into_inner();
    if keys.just_pressed(KeyCode::Space) {
        if bloom.is_some() {
            commands.entity(entity).remove::<Bloom>();
        } else {
            commands.entity(entity).insert(Bloom::NATURAL);
        }
    }
    text.0 = if bloom.is_some() {
        "Bloom: On (Space to toggle)".into()
    } else {
        "Bloom: Off (Space to toggle)".into()
    };
}

Listing 26-2:Bloom 效果——自发光球体与空间切换

Bloom::NATURAL 是默认预设,使用能量守恒(EnergyConserving)模式——辉光从高亮区域"借"亮度,不会让整体变亮。其他预设:

预设效果
Bloom::NATURAL默认。自然、能量守恒
Bloom::ANAMORPHIC模拟变形镜头,水平方向拉伸的辉光
Bloom::OLD_SCHOOL90–2000 年代游戏风格,带阈值预滤波
Bloom::SCREEN_BLUR极端强度,模糊整个屏幕

Bloom 要求相机启用 HDR(Hdr 组件)。Bloom 组件通过 #[require(Hdr)] 自动添加 Hdr,因此手动加不加都行——但理解这个依赖关系很重要。

Bloom 的关键参数

Bloom 结构体的字段控制辉光的行为:

字段类型默认值含义
intensityf320.15辉光强度。0.0 = 无效果,1.0 = 最大散射
low_frequency_boostf320.7低频(大面积模糊)贡献提升
low_frequency_boost_curvaturef320.95低频提升的曲线弯曲度
high_pass_frequencyf321.0高通频率,控制散射角度
prefilterBloomPrefilter见下阈值预滤波
composite_modeBloomCompositeModeEnergyConserving合成模式
scaleVec2(1, 1)辉光拉伸。大 x 值模拟变形镜头

prefilter 包含两个字段:

  • threshold:低于此亮度的像素不产生辉光。NATURAL 预设为 0.0(不过滤),OLD_SCHOOL 为 0.6
  • threshold_softness:阈值边缘的柔和过渡。0.0 = 硬切,1.0 = 完全柔和

composite_mode 有两种:

  • EnergyConserving:辉光从原图"借"亮度,总体能量守恒
  • Additive:辉光叠加到原图上,会增加整体亮度。配合 prefilter 使用时推荐此模式

Tonemapping 的搭配建议

Bloom 文档推荐搭配 TonyMcMapface 色调映射——它会自然地将过亮区域去饱和为白色,与辉光效果配合最协调。AcesFitted 也会产生不错的电影感辉光。