Skip to content

透明与 Alpha 模式

alpha_mode 字段控制材质的透明行为。Bevy 提供六种模式:

模式行为
AlphaMode::Opaque(默认)忽略 alpha,完全不透明
AlphaMode::Mask(threshold)alpha < threshold 则丢弃,否则当不透明
AlphaMode::Blend标准 alpha 混合
AlphaMode::Premultiplied预乘 alpha 混合
AlphaMode::Add加法混合
AlphaMode::Multiply乘法混合

下面是三种最常用模式的并排对比:

rust
use bevy::prelude::*;

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

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let panel = meshes.add(Cuboid::new(2.0, 2.0, 0.05));

    let background = meshes.add(Cuboid::new(1.0, 1.0, 1.0));
    commands.spawn((
        Mesh3d(background.clone()),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.2, 0.5, 0.8),
            perceptual_roughness: 0.3,
            ..default()
        })),
        Transform::from_xyz(0.0, 0.5, -1.5),
    ));

    // Opaque
    commands.spawn((
        Mesh3d(panel.clone()),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgba(0.8, 0.2, 0.2, 1.0),
            alpha_mode: AlphaMode::Opaque,
            ..default()
        })),
        Transform::from_xyz(-2.5, 1.0, 0.0),
    ));

    // Mask
    commands.spawn((
        Mesh3d(panel.clone()),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgba(0.2, 0.8, 0.2, 0.5),
            alpha_mode: AlphaMode::Mask(0.5),
            ..default()
        })),
        Transform::from_xyz(0.0, 1.0, 0.0),
    ));

    // Blend
    commands.spawn((
        Mesh3d(panel.clone()),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgba(0.2, 0.2, 0.8, 0.5),
            alpha_mode: AlphaMode::Blend,
            ..default()
        })),
        Transform::from_xyz(2.5, 1.0, 0.0),
    ));

    // Ground
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(12.0, 12.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.3, 0.3, 0.3),
            perceptual_roughness: 1.0,
            ..default()
        })),
    ));

    // Light
    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.3, 0.0)),
    ));

    // Camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.0, 3.0, 7.0).looking_at(Vec3::new(0.0, 0.8, 0.0), Vec3::Y),
    ));
}

fn rotate_camera(time: Res<Time>, mut query: Query<&mut Transform, With<Camera3d>>) {
    for mut transform in &mut query {
        let angle = time.elapsed_secs() * 0.3;
        let radius = 7.0;
        transform.translation.x = angle.sin() * radius;
        transform.translation.z = angle.cos() * radius;
        transform.translation.y = 3.0;
        *transform = transform.looking_at(Vec3::new(0.0, 0.8, 0.0), Vec3::Y);
    }
}

Listing 24-1:三块面板——Opaque、Mask、Blend 从左到右(examples/listing-24-04.rs)

console
cargo run -p ch24-pbr-materials --example listing-24-04

相机在绕圈,方便你从不同角度观察三块面板的行为差异。

Opaque vs Mask vs Blend

  • Opaque:最高效。着色器可以跳过所有透明计算,深度缓冲直接写入。绝大多数不透明物体用这个。
  • Mask(0.5):alpha ≥ 0.5 的像素完全不透明,< 0.5 的完全丢弃。没有半透明,所以也没有排序问题。适合树叶、铁丝网、像素风特效——边缘是硬切的。
  • Blend:真正的半透明。alpha 值被用于线性插值前景和背景。代价是需要排序(Bevy 按从后到前渲染 blend 物体),且不写深度缓冲。适合玻璃、烟雾、幽灵。

Mask 和 Blend 的选择取决于"边缘应该硬还是软"。树叶的锯齿边缘用 Mask(阈值 0.5),磨砂玻璃的渐变用 Blend。

Add 和 Multiply

  • Add:颜色相加,越叠越亮。黑色不产生效果。适合全息、激光、能量场。
  • Multiply:颜色相乘,越叠越暗。白色不产生效果。适合彩色玻璃、窗膜、有色液体。

排序陷阱

Blend 物体的渲染顺序很重要:Bevy 按物体的 Z 距离从后到前排序,但这是物体级别的排序,不是像素级别。多个半透明物体互相重叠时,可能出现排序错误(A 在 B 前面但画在了后面)。缓解方法:

  • 减少半透明物体的数量和重叠
  • 用 Mask 代替 Blend(如果硬边缘可接受)
  • 对粒子等大量半透明物体,用 Add 模式(不需要排序)