阴影:让物体落地
开阴影后,物体会在地面和其他物体上投下影子,这是理解三维空间关系的关键视觉线索。
启用阴影
三种光源都有 contact_shadows_enabled: bool 字段,设为 true 即可开启阴影。
rust
DirectionalLight {
contact_shadows_enabled: true,
..default()
}点光源和聚光灯同理。
阴影映射资源
阴影的质量受制于阴影贴图的分辨率。Bevy 通过 Resource 类型控制:
| 光源类型 | 阴影贴图 Resource | 默认尺寸 |
|---|---|---|
| DirectionalLight + SpotLight | DirectionalLightShadowMap { size } | 2048 |
| PointLight | PointLightShadowMap { size } | 1024 |
size 必须是 2 的幂。增大尺寸提高阴影精度但降低性能,减小尺寸则反之。
rust
commands.insert_resource(DirectionalLightShadowMap { size: 4096 });Adjust bias to avoid artifacts
depth_bias 与 normal_bias 是两个阴影调优参数:
| 参数 | 含义 | DirectionalLight 默认 | PointLight 默认 |
|---|---|---|---|
shadow_depth_bias | 深度偏移 | 0.02 | 0.08 |
shadow_normal_bias | 法线方向偏移 | 1.8 | 0.6 |
shadow_depth_bias过低 → 阴影表面出现条纹斑驳(shadow acne)shadow_depth_bias过高 → 阴影与物体分离,"飘离"物体(Peter Panning)shadow_normal_bias沿表面法线方向偏移,按纹素大小自动缩放
两个 bias 需要根据场景具体调优,没有通解。
级联阴影
DirectionalLight 默认使用级联阴影映射(CSM),将视锥沿深度切成多段(cascade),每段独立渲染阴影贴图,使近处阴影精度更高。CascadeShadowConfigBuilder 提供配置:
rust
commands.spawn((
DirectionalLight { .. },
CascadeShadowConfigBuilder {
num_cascades: 4,
maximum_distance: 20.0,
first_cascade_far_bound: 4.0,
..default()
}.build(),
));| 参数 | 含义 |
|---|---|
num_cascades | 级联段数,值越高近处阴影越精细 |
maximum_distance | 阴影最大可见距离 |
first_cascade_far_bound | 第一段(最近段)的远平面距离 |
不投阴影的物体
NotShadowCaster 组件标记的实体不产生阴影。可用于性能优化或风格化效果。
阴影过滤方法
ShadowFilteringMethod 是一个组件,挂在相机实体上,控制阴影边框的柔化方式:
| 方法 | 质量 | 性能 |
|---|---|---|
Hardware2x2 | 低 | 最快 |
Gaussian(默认) | 中 | 好 |
Temporal | 高(配合 TAA) | 好 |
rust
// ANCHOR: filter
commands.spawn((
Camera3d::default(),
ShadowFilteringMethod::Gaussian,
));
// ANCHOR_END: filterTemporal 模式需要同时启用时域抗锯齿(TAA)才能达到最佳效果。
完整示例
rust
use bevy::{
light::{CascadeShadowConfigBuilder, NotShadowCaster, ShadowFilteringMethod},
prelude::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, (rotate_light, toggle_shadow_filter))
.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(10.0, 10.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.5, 0.5, 0.5),
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.1, 0.6, 0.9),
..default()
})),
Transform::from_xyz(-1.5, 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.3, 0.2),
..default()
})),
Transform::from_xyz(1.5, 0.5, 0.0),
));
// 不投阴影的物体(标记 NotShadowCaster)
commands.spawn((
Mesh3d(meshes.add(Cylinder::new(0.3, 0.8))),
MeshMaterial3d(materials.add(Color::srgb(0.2, 0.8, 0.2))),
Transform::from_xyz(0.0, 0.4, 1.5),
NotShadowCaster,
));
// DirectionalLight 配置级联阴影
commands.spawn((
DirectionalLight {
illuminance: light_consts::lux::FULL_DAYLIGHT,
contact_shadows_enabled: true,
shadow_depth_bias: 0.02,
shadow_normal_bias: 1.8,
..default()
},
Transform::from_xyz(0.0, 5.0, 0.0)
.looking_at(Vec3::new(0.5, 0.0, -0.5), Vec3::Y),
CascadeShadowConfigBuilder {
num_cascades: 4,
maximum_distance: 15.0,
first_cascade_far_bound: 3.0,
..default()
}
.build(),
));
// 相机(带 ShadowFilteringMethod 组件)
commands.spawn((
Camera3d::default(),
Transform::from_xyz(4.0, 3.0, 6.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
ShadowFilteringMethod::Hardware2x2,
));
}
fn rotate_light(time: Res<Time>, mut query: Query<&mut Transform, With<DirectionalLight>>) {
for mut transform in &mut query {
transform.rotate_y(time.delta_secs() * 0.15);
}
}
fn toggle_shadow_filter(
key_input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut ShadowFilteringMethod>,
) {
if key_input.just_pressed(KeyCode::Space) {
for mut filter in &mut query {
*filter = match *filter {
ShadowFilteringMethod::Hardware2x2 => ShadowFilteringMethod::Gaussian,
ShadowFilteringMethod::Gaussian => ShadowFilteringMethod::Temporal,
ShadowFilteringMethod::Temporal => ShadowFilteringMethod::Hardware2x2,
};
info!("Shadow filtering: {:?}", *filter);
}
}
}Listing 22-5:阴影各项参数的完整演示
运行后空格键循环切换三种阴影过滤方法,可以观察阴影边缘的质量差异。