🦆 延迟渲染管线、微表面 BRDF、法线变换、物体继承树 UI|Wgpu PBR 开发日志 #0002
2025-01-29

法线变换
下文会用 ./ 符号,如 A ./ B ,表示一种运算:对 B 的所有非零元素取倒数后,与 A 的元素逐个相乘。 * 因为旋转和缩放变换都是 3x3 矩阵即可表达,所以我们的法线变换矩阵只需要一个 3x3 矩阵,本节下文中矩阵均为 3x3
-
旋转矩阵(下文称 R)是可逆的,其特性是其逆矩阵等于其转置矩阵,所以对旋转矩阵求逆是安全的: invert(R) = transpose(R) -
缩放矩阵(下文称 S)是一个对角矩阵,其在对角线元素不为零的情况下是可逆的,并且其结果是各元素取倒: invert(S) = 1 ./ S -
根据矩阵积的求逆和转置性质,我们有:
const MIN_SCALE: f32 = 0.001;
pub fn model_normal_matrix(&self) -> (Mat4, Mat3) {
let translation = Matrix4::from_translation(self.position);
let scale = Matrix4::from_nonuniform_scale(self.scale.x, self.scale.y, self.scale.z);
let rotation = Matrix4::from(self.rotation);
#[rustfmt::skip]
let scale_t = Matrix3::new(
1. / self.scale.x.max(Self::MIN_SACLE), 0.0, 0.0,
0.0, 1. / self.scale.y.max(Self::MIN_SACLE), 0.0,
0.0, 0.0, 1. / self.scale.z.max(Self::MIN_SACLE)
);
let model_matrix = translation * rotation * scale;
let normal_matrix = Matrix3::from_cols(
rotation.x.truncate(),
rotation.y.truncate(),
rotation.z.truncate(),
) * scale_t;
(model_matrix, normal_matrix)
}
延迟渲染管线
Write G-Buffer Pass
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &targets, // &[Option<ColorTargetState>]
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
Targets | Texture Format | |
1 | World Pos Tex | Rgba8Unorm |
2 | Normal Tex | Rgba8Unorm |
3 | Base Color Tex | Rgba8Unorm |
5 | PBR Parameters Tex | Rgba8Unorm |
// write_g_buffer.wgsl
struct FragmentOutput{
@location(0) world_pos: vec4<f32>,
@location(1) normal: vec4<f32>,
// For PBR
@location(2) base_color: vec4<f32>,
@location(3) pbr_parameters: vec4<f32>, // 0: Metallic, 1: Roughness, 2: Reflectance, 3: Ambient occlusion
}
...
@fragment
fn fs_main(in: VertexOutput) -> FragmentOutput {
...
Main Pass
Full screen vertex shader
@vertex
fn vs_main(
@builtin(vertex_index) vertex_index: u32,
) -> VertexOutput {
let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;
let clip_position = vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);
return VertexOutput(clip_position, uv);
}
fn render(){
...
render_pass.draw(0..3, 0..1);
...
}

Bindings
wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
compare: None,
lod_min_clamp: 0.0,
lod_max_clamp: 100.0,
..Default::default()
}
0: G-Buffer | |
0 | Sampler |
1 | World Pos Tex |
2 | Normal Tex |
3 | Base Color Tex |
5 | PBR Parameters Tex |
点光源
Radiance of a Point Light
下文将 Radiance 翻译为辐射率。( https://zh.wikipedia.org/wiki/辐射率)
let radiance = intensity / ((decay * pow2(distance)) + 0.001);
遍历光源
let point_lights_num = light.lights_nums.x;
for (var i = 0u; i < point_lights_num; i += 1u) {
let li = point_lights[i];
let world2light_unnorm = li.position.xyz - world_pos;
let world2camera_unnorm = camera.position - world_pos;
let dist = length(world2light_unnorm);
if dist > li.distance { continue; }
let dir = normalize(world2light_unnorm);
let radiance = li.intensity / ((li.decay * pow2(dist)) + 0.001); // + 0.001 for division safety
surface_color += calculate_light(
li.color.xyz,
radiance,
surface,
dir,
normalize(world2camera_unnorm),
);
微表面 BRDF
Parameter | Type & Range |
BaseColor | Linear RGB [0, 1] |
Roughness | Scalar [0, 1] |
Reflectance | Scalar [0, 1] |
Metallic | Scalar [0, 1] |
let f0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + baseColor * metallic;
let diffuse_color = (1.0 - metallic) * base_color;
...
fn perceptual_roughness_to_roughness(perceptual_roughness: f32) -> f32 {
let clamped = clamp(perceptual_roughness, 0.089, 1.0);
return clamped * clamped;
}
更好的 Editor UI

Material Override
参考资料
-
Romain Guy, Mathias Agopian - Physically Based Rendering in Filament https://google.github.io/filament/Filament.html一篇非常好和全面的 PBR 文章,细致地解释了 filament 实时渲染引擎中的 PBR 是如何实现的 -
Blender - 原理化 BSDF https://docs.blender.org/manual/zh-hans/3.5/render/shader_nodes/shader/principled.html -
Boson (2020) - GraphicEngine-Tile-based Deferred Shading using Compute Shader https://myboson.wordpress.com/2020/05/29/graphicengine-tiled-based-deferred-shading-using-compute-shader/ -
Jason L. McKesson (2012) - Normal Transformation https://paroj.github.io/gltut/Illumination/Tut09 Normal Transformation.html