Skip to content

Improve layering in the solari BRDF#24243

Open
dylansechet wants to merge 1 commit into
bevyengine:mainfrom
dylansechet:solari_brdf_layering
Open

Improve layering in the solari BRDF#24243
dylansechet wants to merge 1 commit into
bevyengine:mainfrom
dylansechet:solari_brdf_layering

Conversation

@dylansechet
Copy link
Copy Markdown
Contributor

@dylansechet dylansechet commented May 11, 2026

Objective

Improve the solari BRDF. Split off from #23818.

Solution

The layering approach used in the current brdf to distribute energy between the specular and diffuse lobes doesn't work, for reasons I honestly don't quite understand.

The suggested changes are roughly inspired by OpenPBR's equation 42. We're not using EON though, so this uses Filament's multiscattering correction instead of equation 39.
This formulation has the downside of breaking reciprocity. As far as I can tell the path tracer is unidirectional, so this shouldn't cause issues.


Showcase

White furnace

Main:
main

This PR:
layering


PICA PICA pathtraced

animated

Main:
main

This PR:
pr

@kfc35 kfc35 added A-Rendering Drawing game state to the screen S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 11, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering May 11, 2026
@kfc35 kfc35 added the D-Shaders This code uses GPU shader languages label May 11, 2026
let specular_weight = 1.0 - diffuse_weight;
let rho = lobe_reflectances(F0, ray_hit.material, NdotV);
let specular_weight = luminance(rho.rho_spec) / luminance(rho.rho_spec + rho.rho_diff);
let diffuse_weight = 1 - specular_weight;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let diffuse_weight = 1 - specular_weight;
let diffuse_weight = 1.0 - specular_weight;

@@ -98,10 +98,9 @@ fn pathtrace(@builtin(global_invocation_id) global_id: vec3<u32>) {
fn brdf_pdf(wo: vec3<f32>, wi: vec3<f32>, ray_hit: ResolvedRayHitFull) -> f32 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move brdf_pdf into brdf.wgsl

let LdotH = dot(wi, H);
let NdotV = dot(world_normal, wo);
if NdotL < 0.0001 || NdotH < 0.0001 || LdotH < 0.0001 || NdotV < 0.0001 { return vec3(0.0); }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add back the newline please!

let specular_weight = 1.0 - diffuse_weight;
let rho = lobe_reflectances(F0, material, NdotV);
let specular_weight = luminance(rho.rho_spec) / luminance(rho.rho_spec + rho.rho_diff);
let diffuse_weight = 1 - specular_weight;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let diffuse_weight = 1 - specular_weight;
let diffuse_weight = 1.0 - specular_weight;

// Hemispherical reflectance of each lobe
fn lobe_reflectances(F0: vec3<f32>, material: ResolvedMaterial, NdotV: f32) -> LobeReflectances {
let F_ab = F_AB(material.perceptual_roughness, NdotV);
let ms_factor = 1.0 / (F_ab.x + F_ab.y) - 1.0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let ms_factor = 1.0 / (F_ab.x + F_ab.y) - 1.0;
let multi_scattering_factor = 1.0 / (F_ab.x + F_ab.y) - 1.0;

Comment on lines +19 to +22
struct LobeReflectances {
rho_spec: vec3<f32>,
rho_diff: vec3<f32>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct LobeReflectances {
rho_spec: vec3<f32>,
rho_diff: vec3<f32>,
}
struct LobeReflectances {
specular: vec3<f32>,
diffuse: vec3<f32>,
}


return diffuse_color * layering * NdotL;
let rho = lobe_reflectances(F0, material, NdotV);
return rho.rho_diff / PI * NdotL;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is PI here, and not part of rho_diffuse?

return rho.rho_diff / PI * NdotL;
}

fn evaluate_specular_brdf(wo: vec3<f32>, wi: vec3<f32>, world_normal: vec3<f32>, material: ResolvedMaterial) -> vec3<f32> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anything in evaluate_specular_brdf need to change, similar to how evaluate_diffuse_brdf relies on rho_diffuse?


// Mirror specular is a delta function
if material.roughness <= MIRROR_ROUGHNESS_THRESHOLD {
return EvaluateAndSampleBrdfResult(wi, rho.rho_spec / specular_weight, 1.0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we return INF for the PDF before for mirrors? I'm trying to remember how to handle mirrors, it's hurting my brain...

let diffuse_weight = mix(df, 0.0, ray_hit.material.metallic);
let specular_weight = 1.0 - diffuse_weight;
let rho = lobe_reflectances(F0, ray_hit.material, NdotV);
let specular_weight = luminance(rho.rho_spec) / luminance(rho.rho_spec + rho.rho_diff);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the denominator is 0, because you have a pure black material?

@JMS55
Copy link
Copy Markdown
Contributor

JMS55 commented May 13, 2026

Btw would love to know the code you used for the white furnace test for solari - I lost mine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen D-Shaders This code uses GPU shader languages S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

3 participants