Skip to content

金属度与粗糙度——PBR 直觉

第 21 章的材质墙只用了十五颗球,横轴粗糙度三档、纵轴金属感五档。这次我们拉满:五五二十五颗,粗糙度从 0.05 到 1.0、金属感从 0.0 到 1.0,每个交叉点一颗:

rust
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.4).mesh().uv(32, 18));

    let roughness_values = [0.05, 0.25, 0.5, 0.75, 1.0];
    let metallic_values = [0.0, 0.25, 0.5, 0.75, 1.0];

    for (row, &metallic) in metallic_values.iter().enumerate() {
        for (col, &roughness) in roughness_values.iter().enumerate() {
            let x = (col as f32 - 2.0) * 1.2;
            let z = (row as f32 - 2.0) * 1.2;

            commands.spawn((
                Mesh3d(sphere.clone()),
                MeshMaterial3d(materials.add(StandardMaterial {
                    base_color: Color::srgb(0.8, 0.2, 0.1),
                    metallic,
                    perceptual_roughness: roughness,
                    ..default()
                })),
                Transform::from_xyz(x, 0.4, z),
            ));
        }
    }

    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(8.0, 8.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            base_color: Color::srgb(0.15, 0.15, 0.15),
            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.8, 0.4, 0.0)),
    ));

    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(4.0, 4.5, 6.0).looking_at(Vec3::new(0.0, 0.2, 0.0), Vec3::Y),
    ));
}

Listing 24-1:五乘五材质墙——粗糙度与金属感的完整交叉(examples/listing-24-01.rs)

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

读图同第 21 章的方法:从右下角(非金属、最糙)往左上角(金属、最光滑)读。右下角是粉笔——均匀的哑光、没有高光;左下角是上了亮光漆的瓷——白色高光锐利地钉在球面上;右上角是磨砂铜——暖金色铺满;左上角是镜面金属——在没有环境贴图的世界里,它忠实地反射虚空,所以最暗。

二十五颗球比十五颗多出的,是对中间地带的覆盖:metallic = 0.5 的那一行尤其值得看。它既不完全是"非金属的白高光",也不完全是"金属的有色反射",而是两者的混合——现实里"锈蚀的金属""沾了灰的铝"就是这个区域。metallic 的设计意图就是让 0 和 1 做分界,中间值留给"混合表面"这类特殊情况。

粗糙度的刻度

perceptual_roughness 的 0.089 下限在着色器里钳制,写 0.0 也等同于 0.089——这是浮点精度的护栏,不是能省就省的"光滑无限大"。0.5 是 Blender 默认值,视觉上恰好是"半糙"——不高不低,适合做初始调参的锚点。1.0 则是彻底的漫反射面,高光完全消失。

一个常见误解:粗糙度越高,画面越"暗"。不是——粗糙度只是把高光摊散了,总能量不变。哑光白纸(roughness = 1.0)依然白,只是没有那粒亮斑。真正影响明暗的是金属感——金属没有漫反射,一旦粗糙度低到接近镜面,它反射的只有环境。

从"看"到"用"

在实际项目里,二十五种组合不会同时出现,但调参的路径是一样的:先定 metallic(这东西是金属还是非金属?),再调 perceptual_roughness(想要多光滑?),最后微调 base_color。这三步的顺序不能反——如果先选了漂亮的金色、再把 metallic 从 0 拉到 1,金色会变暗变脏,因为金属的固有色只作用于反射、不作用于漫反射。