Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions native/shared/src/renderer/froxel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,8 @@ const CLUSTERED_LOOP: &str = "
let l = to_light / dist;
let atten = 1.0 - (dist / range);
let atten2 = atten * atten;
if (has_spec_gloss) {
lit += shade_specular_glossiness_pbr(
n, v, l, pl.color.rgb, pl.color.w * atten2,
base_color, authored_specular, roughness,
);
} else {
lit += shade_pbr(n, v, l, pl.color.rgb, pl.color.w * atten2,
base_color, metallic, roughness);
}
lit += shade_pbr_f0(n, v, l, pl.color.rgb, pl.color.w * atten2,
light_diffuse, light_f0, roughness);
}
}
";
Expand All @@ -191,10 +184,15 @@ pub(super) fn clustered_scene_shader(source: &str) -> String {
.expect("scene shader missing END marker")
+ end_marker.len();
let clustered_loop = if source.contains("fn shade_layered_base_pbr(") {
CLUSTERED_LOOP.replace(
"shade_pbr(n, v,",
"shade_layered_pbr(layered_surface, n, v,",
)
CLUSTERED_LOOP
.replace(
"shade_pbr_f0(n, v,",
"shade_layered_pbr(layered_surface, n, v,",
)
.replace(
"light_diffuse, light_f0, roughness",
"base_color, metallic, roughness",
)
} else {
CLUSTERED_LOOP.to_owned()
};
Expand Down
9 changes: 8 additions & 1 deletion native/shared/src/renderer/layered_pbr_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,17 @@ fn layered_secondary_uv(in: VertexOutputScene) -> vec2<f32> {
"direct diffuse complement",
);

// The direct lights take the layered BRDF, from the base colour and
// metallic (glTF's layered extensions are metallic/roughness only; the
// specular/glossiness extension doesn't combine with them).
source = source.replace(
"shade_pbr(n, v,",
"shade_pbr_f0(n, v,",
"shade_layered_pbr(layered_surface, n, v,",
);
source = source.replace(
"light_diffuse, light_f0, roughness",
"base_color, metallic, roughness",
);
assert_eq!(
source
.matches("shade_layered_pbr(layered_surface, n, v,")
Expand Down
52 changes: 23 additions & 29 deletions native/shared/src/renderer/shaders/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,14 @@ fn shade_pbr(
// failure case. Keeping the authored F0 here preserves both lobes while
// sharing the established GGX distribution, visibility, energy bound, and
// roughness policy with the ordinary material path.
fn shade_specular_glossiness_pbr(
// Direct GGX for a surface given as a diffuse colour and an F0: both the
// specular/glossiness workflow (authored) and metallic/roughness (diffuse =
// base * (1 - metallic), F0 = mix(0.04, base, metallic), exactly shade_pbr)
// light through this one function, so the scene shader inlines one BRDF per
// light instead of one per workflow (the second copy was 8 ms of a 38 ms
// main pass on a mobile GPU, Pixel 4a). shade_pbr stays the metallic form
// the layered-material variant is derived from.
fn shade_pbr_f0(
n: vec3<f32>,
v: vec3<f32>,
l_dir: vec3<f32>,
Expand Down Expand Up @@ -1032,6 +1039,14 @@ fn shade_main_scene(in: VertexOutputScene, front_facing: bool) -> SceneOut {
let dielectric_floor = select(0.15, 0.045, has_spec_gloss);
var roughness = max(roughness_raw,
dielectric_floor * (1.0 - metallic));
// What the lights see of either workflow: a diffuse colour and an F0
// (see shade_pbr_f0).
let light_diffuse = select(base_color * (1.0 - metallic), base_color, has_spec_gloss);
let light_f0 = select(
mix(vec3<f32>(0.04), base_color, metallic),
authored_specular,
has_spec_gloss,
);

// Specular antialiasing. Two sources of variance are folded into
// GGX α² as additive corrections:
Expand Down Expand Up @@ -1120,16 +1135,9 @@ fn shade_main_scene(in: VertexOutputScene, front_facing: bool) -> SceneOut {
lit += base_color / PI * lighting.light_color.rgb * lighting.light_dir.w
* ndl_wrap * direct_shadow;
} else {
if (has_spec_gloss) {
lit += shade_specular_glossiness_pbr(
n, v, legacy_dir, lighting.light_color.rgb,
lighting.light_dir.w, base_color, authored_specular, roughness,
) * direct_shadow;
} else {
lit += shade_pbr(n, v, legacy_dir, lighting.light_color.rgb,
lighting.light_dir.w, base_color, metallic, roughness)
* direct_shadow;
}
lit += shade_pbr_f0(n, v, legacy_dir, lighting.light_color.rgb,
lighting.light_dir.w, light_diffuse, light_f0, roughness)
* direct_shadow;
}

// Foliage backlit transmission — sun bleeding THROUGH alpha-cut leaf cards
Expand All @@ -1149,15 +1157,8 @@ fn shade_main_scene(in: VertexOutputScene, front_facing: bool) -> SceneOut {
for (var i = 0u; i < dir_count; i++) {
let dl = lighting.dir_lights[i];
let l = normalize(dl.direction.xyz);
if (has_spec_gloss) {
lit += shade_specular_glossiness_pbr(
n, v, l, dl.color.rgb, dl.direction.w,
base_color, authored_specular, roughness,
);
} else {
lit += shade_pbr(n, v, l, dl.color.rgb, dl.direction.w,
base_color, metallic, roughness);
}
lit += shade_pbr_f0(n, v, l, dl.color.rgb, dl.direction.w,
light_diffuse, light_f0, roughness);
}

// BEGIN-POINT-LIGHT-LOOP (replaced by the froxel-clustered variant
Expand All @@ -1174,15 +1175,8 @@ fn shade_main_scene(in: VertexOutputScene, front_facing: bool) -> SceneOut {
let l = to_light / dist;
let atten = 1.0 - (dist / range);
let atten2 = atten * atten;
if (has_spec_gloss) {
lit += shade_specular_glossiness_pbr(
n, v, l, pl.color.rgb, pl.color.w * atten2,
base_color, authored_specular, roughness,
);
} else {
lit += shade_pbr(n, v, l, pl.color.rgb, pl.color.w * atten2,
base_color, metallic, roughness);
}
lit += shade_pbr_f0(n, v, l, pl.color.rgb, pl.color.w * atten2,
light_diffuse, light_f0, roughness);
}
}
// END-POINT-LIGHT-LOOP
Expand Down
13 changes: 12 additions & 1 deletion native/shared/src/renderer/shaders/core_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ fn scene_vertex_shader_preserves_the_missing_tangent_sentinel() {
fn textured_specular_glossiness_keeps_authored_diffuse_and_f0_independent() {
wgpu::naga::front::wgsl::parse_str(SCENE_SHADER)
.unwrap_or_else(|error| panic!("ordinary scene WGSL failed: {error:?}"));
assert!(SCENE_SHADER.contains("fn shade_specular_glossiness_pbr("));
// Both workflows light through one BRDF taking a diffuse colour and an F0.
assert!(SCENE_SHADER.contains("fn shade_pbr_f0("));
assert!(!SCENE_SHADER.contains("fn shade_specular_glossiness_pbr("));
assert!(SCENE_SHADER.contains(
"let light_diffuse = select(base_color * (1.0 - metallic), base_color, has_spec_gloss);"
));
assert_eq!(
SCENE_SHADER
.matches("light_diffuse, light_f0, roughness")
.count(),
3
);
assert!(SCENE_SHADER.contains("let f0 = select(mr_f0, authored_specular, has_spec_gloss);"));
assert!(
SCENE_SHADER.contains("let diffuse_weight = select(1.0 - metallic, 1.0, has_spec_gloss);")
Expand Down
Loading