Skip to content

环境光与反射环境贴图

场景中除了直接光源,还需要对暗部的照明进行兜底。Bevy 提供两层兜底机制:GlobalAmbientLight 提供廉价的无方向填充光;EnvironmentMapLight 提供基于 HDRI 贴图的反射与漫反射照明。

GlobalAmbientLight

GlobalAmbientLight 是一个 Resource,作用于整个场景。它的光照无方向、无阴影、不计衰减——纯粹给背光面一个"看得见"的亮度:

rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        // 默认 GlobalAmbientLight 亮度 80,此处调暗以凸显环境光的效果
        .insert_resource(GlobalAmbientLight {
            color: Color::srgb(0.2, 0.2, 0.5),
            brightness: 60.0,
            ..default()
        })
        .add_systems(Startup, setup)
        .add_systems(Update, (toggle_ambient, adjust_brightness))
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // 地面
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(8.0, 8.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.4, 0.4, 0.4),
            perceptual_roughness: 1.0,
            ..default()
        })),
    ));

    // 展示物体
    commands.spawn((
        Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.2, 0.6, 0.8),
            ..default()
        })),
        Transform::from_xyz(-1.2, 0.5, 0.0),
    ));
    commands.spawn((
        Mesh3d(meshes.add(Sphere::new(0.5).mesh().uv(32, 18))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.9, 0.1, 0.1),
            ..default()
        })),
        Transform::from_xyz(1.2, 0.5, 0.0),
    ));

    // 本场景没有 DirectionalLight、PointLight、SpotLight
    // 只有 GlobalAmbientLight 提供全场景的兜底照明

    // 相机
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(4.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));
}

fn toggle_ambient(key_input: Res<ButtonInput<KeyCode>>, mut ambient: ResMut<GlobalAmbientLight>) {
    if key_input.just_pressed(KeyCode::Space) {
        ambient.brightness = if ambient.brightness > 1.0 { 0.0 } else { 60.0 };
    }
}

fn adjust_brightness(
    key_input: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut ambient: ResMut<GlobalAmbientLight>,
) {
    if key_input.pressed(KeyCode::ArrowUp) {
        ambient.brightness += time.delta_secs() * 60.0;
    }
    if key_input.pressed(KeyCode::ArrowDown) {
        ambient.brightness = (ambient.brightness - time.delta_secs() * 60.0).max(0.0);
    }
}

Listing 22-4:GlobalAmbientLight 的设置与运行时调节

字段含义
color环境光颜色
brightness亮度乘数,单位 cd/m²
affects_lightmapped_meshes是否影响已烘焙光照贴图的网格

默认值 color: WHITEbrightness: 80.0。Listing 22-4 将其调低并设成蓝紫色以凸显效果。

运行时通过 Space 键开关、上下键调节亮度,可以直观对比环境光有无的差别。

各光源的环境光策略

  • 有 DirectionalLight/PointLight/SpotLight 的场景brightness 设为 20—80 即可,避免盖过直接光的方向感
  • 只有 AmbientLight 的场景:展示物体在没有直接光时的形态,适合用作调试或风格化渲染
  • brightness: 0.0:无环境光,背光面完全纯黑。需要与直接光配合使用

EnvironmentMapLight

EnvironmentMapLight 提供基于图像的照明(IBL)。它用一对立方体贴图(diffuse + specular)来模拟环境反射:

rust
use bevy::prelude::*;

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

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    asset_server: Res<AssetServer>,
) {
    // 地面
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(6.0, 6.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.3, 0.3, 0.3),
            perceptual_roughness: 0.8,
            ..default()
        })),
    ));

    // 高反射金属球——环境贴图在它身上最明显
    commands.spawn((
        Mesh3d(meshes.add(Sphere::new(0.8).mesh().uv(64, 36))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.8, 0.8, 0.8),
            metallic: 1.0,
            perceptual_roughness: 0.05,
            ..default()
        })),
        Transform::from_xyz(0.0, 0.8, 0.0),
    ));

    // EnvironmentMapLight:用 .hdr 等距柱面图生成环境贴图。
    // 把 EnvironmentMapLight 挂在相机上,作用于整个画面。
    // 需要的 .hdr 文件可用工具(如 Blender、HDRI Haven)生成或下载。
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(3.5, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
        EnvironmentMapLight {
            diffuse_map: asset_server.load("environment_maps/pisa_diffuse_8k.jpg"),
            specular_map: asset_server.load("environment_maps/pisa_specular_8k.jpg"),
            intensity: 500.0,
            ..default()
        },
    ));
}

Listing 22-6:EnvironmentMapLight——需要 .hdr 贴图文件

EnvironmentMapLight 作为组件挂在相机实体上,为整个画面提供环境反射信息。标 intensity 控制反射强度。

前置条件:需要准备一张 .hdr 格式的等距柱面投影环境贴图。工具链包括 Blender(导出 HDR)、HDRI Haven(下载)、或 Bevy 官方示例仓库中的测试贴图。

EnvironmentMapLight 对高反射材质的效果最为明显——金属球上清晰倒映出周围环境。光探针(LightProbe) 是其进阶用法,能将环境照明限定在场景的特定空间区域,在第 24 章结合 PBR 材质深入讨论。