法线贴图与自发光
法线贴图——假凹凸
法线贴图(Normal Map)用一张 RGB 图编码每个像素的"面朝向",让平坦的网格看起来有凹凸细节,而不需要额外的几何。在 Bevy 里挂到 normal_map_texture 字段即可:
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>,
) {
let normal_handle: Handle<Image> = asset_server.load_with_settings(
"textures/parallax_example/cube_normal.png",
|settings: &mut bevy::image::ImageLoaderSettings| settings.is_srgb = false,
);
let sphere_mesh = meshes.add(Sphere::new(0.8).mesh().uv(64, 36));
commands.spawn((
Mesh3d(sphere_mesh.clone()),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.6, 0.6, 0.6),
perceptual_roughness: 0.4,
normal_map_texture: Some(normal_handle),
double_sided: true,
..default()
})),
Transform::from_xyz(-1.2, 0.8, 0.0),
));
commands.spawn((
Mesh3d(sphere_mesh.clone()),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.6, 0.6, 0.6),
perceptual_roughness: 0.4,
..default()
})),
Transform::from_xyz(1.2, 0.8, 0.0),
));
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(8.0, 8.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.2, 0.2, 0.2),
perceptual_roughness: 1.0,
..default()
})),
));
commands.spawn((
DirectionalLight {
illuminance: light_consts::lux::FULL_DAYLIGHT,
contact_shadows_enabled: true,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -0.6, 0.4, 0.0)),
));
commands.spawn((
Camera3d::default(),
Transform::from_xyz(3.0, 2.5, 4.0).looking_at(Vec3::new(0.0, 0.4, 0.0), Vec3::Y),
));
}Listing 24-2:左球挂法线贴图,右球纯平——同一光源下凹凸差异立现(examples/listing-24-02.rs)
cargo run -p ch24-pbr-materials --example listing-24-02左边那颗球表面的凹凸是假的——几何上它仍然是完美球面,但法线贴图让光照以为每个像素的朝向不同,于是高光和阴影在表面"滚动"。右边的对照球是同一材质、同一粗糙度,没有法线贴图,光滑得像乒乓球。
法线贴图三件套
Bevy 的 PBR 着色器要求三样东西同时就位,法线贴图才生效:
- 法线贴图纹理——
normal_map_texture - UV 坐标——网格顶点必须有 UV,否则贴图不知道往哪铺
- 切线(Tangents)——网格顶点必须有切线数据,用来把法线从贴图空间变换到世界空间
如果法线贴图挂了但效果不明显(球面还是平的),大概率是缺切线。解决方法:
mesh.generate_tangents().unwrap();法线贴图是 DirectX 格式(Y 轴朝下)还是 OpenGL 格式(Y 轴朝上)?Bevy 默认按 OpenGL 处理。如果你的贴图是 DirectX 格式(比如从 Substance Painter 导出),设置 flip_normal_map_y: true。
加载时注意 is_srgb
法线贴图存的是方向向量,不是颜色。如果按 sRGB 加载,GPU 会对值做 gamma 反转,法线方向就歪了。加载时必须关掉 sRGB:
asset_server.load_with_settings(
"path/to/normal.png",
|settings: &mut ImageLoaderSettings| settings.is_srgb = false,
);自发光——在黑暗里亮起来
emissive 字段让材质表面"自己发光"——不是照别人,是照相机。适合做屏幕、指示灯、霓虹灯管。
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>>,
) {
let sphere = meshes.add(Sphere::new(0.5).mesh().uv(32, 18));
let emissive_colors = [
(Color::srgb(1.0, 0.0, 0.0), "红"),
(Color::srgb(0.0, 1.0, 0.0), "绿"),
(Color::srgb(0.0, 0.0, 1.0), "蓝"),
(Color::WHITE, "白"),
];
for (i, (color, _name)) in emissive_colors.iter().enumerate() {
let x = (i as f32 - 1.5) * 1.4;
commands.spawn((
Mesh3d(sphere.clone()),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.1, 0.1, 0.1),
emissive: LinearRgba::from(*color) * 5.0,
..default()
})),
Transform::from_xyz(x, 0.5, 0.0),
));
}
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(3.0, 0.05, 3.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.1, 0.1, 0.1),
perceptual_roughness: 0.8,
..default()
})),
Transform::from_xyz(0.0, -0.01, 0.0),
));
commands.spawn((
Camera3d::default(),
Transform::from_xyz(3.5, 2.5, 5.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
));
}Listing 24-3:四颗自发光球——无灯光环境下自见(examples/listing-24-03.rs)
cargo run -p ch24-pbr-materials --example listing-24-03注意这个例子里没有添加任何灯光。四颗球在完全黑暗的环境里依然可见,全靠 emissive。值的单位是 nits(坎德拉每平方米),LinearRgba::BLACK(默认)表示不发光。颜色通道可以远超 1.0——比如 LinearRgba::rgb(100.0, 0.0, 0.0) 是极亮的红光,后处理的 bloom 效果会在它周围产生辉光。
emissive 与 emissive_texture 的关系是乘法:最终自发光 = emissive × emissive_texture。如果只想用贴图控制发光区域,把 emissive 设为白色。
自发光不会照亮周围物体——它只影响本材质的像素值。想要物体真的当灯用,请用 PointLight 或 SpotLight。自发光的定位是"视觉标记",不是"光源替代品"。