景深与色散
本节介绍两种模拟相机光学"缺陷"的后处理效果:景深(Depth of Field)让聚焦点之外的物体模糊,色散(Chromatic Aberration)让画面边缘出现颜色分离。两者叠加使用时,画面质感会显著提升。
DepthOfField:景深
景深模拟真实相机的对焦行为——只有焦平面上的物体清晰,距离焦平面越远越模糊。
rust
use bevy::{
core_pipeline::tonemapping::Tonemapping,
post_process::{
bloom::Bloom,
dof::{DepthOfField, DepthOfFieldMode},
},
prelude::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, adjust_dof)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3d::default(),
Tonemapping::TonyMcMapface,
Bloom::NATURAL,
Transform::from_xyz(0.0, 2.0, 6.0).looking_at(Vec3::ZERO, Vec3::Y),
DepthOfField {
mode: DepthOfFieldMode::Gaussian,
focal_distance: 6.0,
aperture_f_stops: 0.5,
..default()
},
));
commands.spawn((
DirectionalLight {
illuminance: 15000.0,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, -0.5, -0.5)),
));
let red = materials.add(Color::srgb(0.8, 0.1, 0.1));
let green = materials.add(Color::srgb(0.1, 0.8, 0.1));
let blue = materials.add(Color::srgb(0.1, 0.1, 0.8));
let sphere = meshes.add(Sphere::new(0.5));
let positions = [
(Vec3::new(-3.0, 0.5, -2.0), red),
(Vec3::new(0.0, 0.5, 0.0), green),
(Vec3::new(3.0, 0.5, 2.0), blue),
];
for (pos, mat) in positions {
commands.spawn((
Mesh3d(sphere.clone()),
MeshMaterial3d(mat),
Transform::from_translation(pos),
));
}
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))),
MeshMaterial3d(materials.add(Color::srgb(0.15, 0.15, 0.15))),
));
commands.spawn((
Text::default(),
Node {
position_type: PositionType::Absolute,
bottom: px(12),
left: px(12),
..default()
},
));
}
fn adjust_dof(
mut dof: Single<&mut DepthOfField>,
mut text: Single<&mut Text>,
keys: Res<ButtonInput<KeyCode>>,
) {
if keys.pressed(KeyCode::ArrowUp) {
dof.focal_distance += 0.05;
}
if keys.pressed(KeyCode::ArrowDown) {
dof.focal_distance = (dof.focal_distance - 0.05).max(0.01);
}
if keys.pressed(KeyCode::ArrowRight) {
dof.aperture_f_stops += 0.01;
}
if keys.pressed(KeyCode::ArrowLeft) {
dof.aperture_f_stops = (dof.aperture_f_stops - 0.01).max(0.05);
}
if keys.just_pressed(KeyCode::Space) {
dof.mode = match dof.mode {
DepthOfFieldMode::Bokeh => DepthOfFieldMode::Gaussian,
DepthOfFieldMode::Gaussian => DepthOfFieldMode::Bokeh,
};
}
let mode_name = match dof.mode {
DepthOfFieldMode::Bokeh => "Bokeh",
DepthOfFieldMode::Gaussian => "Gaussian",
};
text.0 = format!(
"DoF Mode: {mode_name} (Space)\n\
Focal distance: {:.2} m (Up/Down)\n\
Aperture: f/{:.2} (Left/Right)",
dof.focal_distance, dof.aperture_f_stops,
);
}Listing 26-3:景深效果——焦点前后物体的模糊
DepthOfField 组件
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
mode | DepthOfFieldMode | Gaussian | 效果模式 |
focal_distance | f32 | — | 焦点距离(米) |
aperture_f_stops | f32 | — | 光圈 f 值。越小模糊越强 |
sensor_height | f32 | 0.01866 | 传感器高度(米)。默认 18.66mm,对应 Super 35 胶片 |
max_circle_of_confusion_diameter | f32 | — | 弥散圆最大直径(像素) |
max_depth | f32 | — | 最大考虑深度。防止天空盒产生无限大模糊 |
DepthOfFieldMode 有两种模式:
Gaussian:默认。高斯模糊,速度快,兼容 WebGPUBokeh:散景模拟,更接近真实镜头效果,但不兼容 WebGPU
aperture_f_stops 越小,景深越浅(背景越模糊)。f/0.5 是极端值,f/8.0 则几乎全画面清晰。实际使用时需要根据场景尺度调整。
与 Bloom 的配合
景深和 Bloom 经常一起使用——Bloom 让高亮区域发光,景深让焦点外的区域模糊,两者叠加产生梦幻般的镜头感。Listing 26-3 同时启用了两者。
ChromaticAberration:色散
色散模拟真实镜头中不同波长的光折射角度略有差异的现象——画面边缘会出现红/蓝颜色分离的条纹。
rust
use bevy::{
post_process::effect_stack::ChromaticAberration,
prelude::*,
camera::Hdr,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, adjust_chromatic_aberration)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3d::default(),
Hdr,
Transform::from_xyz(2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
ChromaticAberration::default(),
));
commands.spawn((
DirectionalLight {
illuminance: 15000.0,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, -0.5, -0.5)),
));
let mat = materials.add(StandardMaterial {
base_color: Color::srgb(0.9, 0.9, 0.9),
..default()
});
commands.spawn((
Mesh3d(meshes.add(Sphere::new(1.0))),
MeshMaterial3d(mat),
Transform::from_xyz(0.0, 1.0, 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,
bottom: px(12),
left: px(12),
..default()
},
));
}
fn adjust_chromatic_aberration(
mut ca: Single<&mut ChromaticAberration>,
mut text: Single<&mut Text>,
keys: Res<ButtonInput<KeyCode>>,
) {
if keys.pressed(KeyCode::ArrowRight) {
ca.intensity += 0.002;
}
if keys.pressed(KeyCode::ArrowLeft) {
ca.intensity -= 0.002;
}
ca.intensity = ca.intensity.clamp(0.0, 0.4);
text.0 = format!(
"Chromatic Aberration: {:.3}\n(Left/Right arrows to adjust)",
ca.intensity,
);
}Listing 26-4:色散效果——调整强度观察边缘颜色分离
ChromaticAberration 组件
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
intensity | f32 | 0.02 | 色散强度,占屏幕尺寸的比例 |
max_samples | u32 | 8 | 最大采样数。越大越平滑但越慢 |
color_lut | Option<Handle<Image>> | None | 颜色查找纹理。默认红-绿-蓝三色 |
intensity 的推荐范围是 0.0–0.4。默认 0.02 是一个微妙的效果,适合大部分场景。超过 0.2 时色散会非常明显,通常用于短暂的冲击效果(如受到攻击时)。
色散效果在画面中心几乎不可见,越靠近边缘越明显——这与真实镜头的行为一致。