HDR 与 Tonemapping
显示器能显示的颜色范围是有限的(0.0–1.0),但真实世界的光照强度远超这个范围——太阳、灯泡、自发光材质的亮度可能是普通表面的几百倍。HDR(High Dynamic Range,高动态范围)渲染让这些超高亮度值在计算过程中保留下来,最终通过 Tonemapping(色调映射)压缩回显示器能显示的范围。
启用 HDR
在 Bevy 中,HDR 是一个组件标记——把 Hdr 加到相机实体上即可:
rust
use bevy::{
core_pipeline::tonemapping::Tonemapping,
prelude::*,
camera::Hdr,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, switch_tonemapping)
.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(2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((
DirectionalLight {
illuminance: 15000.0,
..default()
},
Transform::from_rotation(Quat::from_euler(
EulerRot::ZYX,
0.0,
-0.5,
-0.5,
)),
));
let bright = materials.add(StandardMaterial {
base_color: Color::srgb(0.8, 0.2, 0.1),
emissive: LinearRgba::rgb(5.0, 1.0, 0.5),
..default()
});
let normal = materials.add(Color::srgb(0.3, 0.5, 0.3));
let sphere = meshes.add(Sphere::new(0.5));
commands.spawn((
Mesh3d(sphere.clone()),
MeshMaterial3d(bright),
Transform::from_xyz(-1.2, 0.5, 0.0),
));
commands.spawn((
Mesh3d(sphere),
MeshMaterial3d(normal),
Transform::from_xyz(1.2, 0.5, 0.0),
));
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
MeshMaterial3d(materials.add(Color::srgb(0.2, 0.2, 0.2))),
));
commands.spawn((
Text::default(),
Node {
position_type: PositionType::Absolute,
top: px(12),
left: px(12),
..default()
},
));
}
fn switch_tonemapping(
mut text: Single<&mut Text>,
tonemapping: Single<&Tonemapping>,
keys: Res<ButtonInput<KeyCode>>,
) {
let name = match **tonemapping {
Tonemapping::None => "None",
Tonemapping::Reinhard => "Reinhard",
Tonemapping::ReinhardLuminance => "ReinhardLuminance",
Tonemapping::AcesFitted => "AcesFitted",
Tonemapping::AgX => "AgX",
Tonemapping::SomewhatBoringDisplayTransform => "SomewhatBoringDisplayTransform",
Tonemapping::TonyMcMapface => "TonyMcMapface",
Tonemapping::BlenderFilmic => "BlenderFilmic",
Tonemapping::KhronosPbrNeutral => "KhronosPbrNeutral",
};
text.0 = format!("Tonemapping: {name}\n(1-8 to switch)");
let _ = keys;
}Listing 26-1:HDR 相机与 TonyMcMapface 色调映射
Hdr 组件告诉渲染管线使用浮点帧缓冲区,保留超出 0–1 范围的亮度值。没有 Hdr,所有超过 1.0 的颜色会被直接截断,后续的 Bloom 等效果将无法正确工作。
Tonemapping 枚举
Tonemapping 是一个组件,挂在相机实体上,决定如何把 HDR 值压缩到显示器范围。Bevy 内置了多种算法:
| 枚举值 | 特点 |
|---|---|
None | 不做映射,直接截断。高亮区域过曝、颜色失真 |
Reinhard | 经典算法,简单但会丢失亮部细节 |
ReinhardLuminance | 基于亮度的 Reinhard 变体,保留色相更好 |
AcesFitted | ACES 电影级映射,对比度高、色彩风格化明显 |
AgX | 中性、低色相偏移,适合写实风格(需 tonemapping_luts feature) |
TonyMcMapface | Bevy 默认。中性、低对比度、亮部自然去饱和 |
BlenderFilmic | Blender 默认的电影级映射 |
默认值是 TonyMcMapface,它在大多数场景下表现均衡。如果想要更强烈的电影感,可以尝试 AcesFitted;追求写实中性则选 AgX。
自发光与 HDR 的配合
HDR 渲染的意义在于保留亮度信息。StandardMaterial 的 emissive 字段接受 LinearRgba,其分量可以远超 1.0:
rust
StandardMaterial {
emissive: LinearRgba::rgb(5.0, 1.0, 0.5), // 亮度是普通白色的 5 倍
..default()
}这些超亮值在 Tonemapping 之前保留完整精度,是 Bloom 效果的前提——Bloom 需要识别"比周围亮得多"的区域来生成辉光。