Conversation
…ctric) The base-class fallback of eval_diffuse_reflectance() evaluates the BSDF at wo = (0, 0, 1) and multiplies by pi. This is exact for a Lambertian lobe, but for microfacet-only BSDFs it evaluates the glossy lobe instead: when the view direction approaches the surface normal, the half-vector aligns with the specular peak and the returned value spikes (with alpha = 0.1, roughdielectric returns ~0.97 and roughconductor ~24 at cos(theta) ~ 1, falling to 0 within a few degrees). In renders this paints a bright, view-dependent blob into the albedo AOV wherever a glossy surface faces the camera, violating the contract of a view-independent reflectance in [0, 1] that denoisers rely on. Override eval_diffuse_reflectance() to return 0 in roughconductor and roughdielectric (no diffuse lobe), document the behaviour in bsdf.h, and add regression tests covering several view angles.
Returning 0 makes the albedo AOV uninformative on rough metals: denoisers lose the texture and color edges of the material. Return the normal-incidence Fresnel reflectance (F0), modulated by specular_reflectance when present, instead. This is the value denoisers such as OIDN recommend as the albedo of a metal: view-independent, bounded in [0, 1], and carrying the base color of colored metals (gold, copper). roughdielectric keeps returning 0, since a transmissive microfacet surface has no meaningful base reflectance.
A rough dielectric reflects or transmits essentially all incident energy, so 1 -- tinted by specular_transmittance when present -- is the appropriate view-independent albedo for denoising AOVs (the convention denoisers such as OIDN recommend for transmissive surfaces). Returning 0 made the albedo feature claim the surface reflects nothing, which is the opposite of its actual throughput.
…ents The F0 / 1 fallback values for roughconductor and roughdielectric follow the Open Image Denoise documentation for metallic and dielectric surfaces; say so where they are implemented.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
While generating albedo G-buffers with Mitsuba for Monte Carlo denoising
research, I noticed the albedo AOV was contaminated on glossy-only surfaces:
a bright, camera-dependent blob appears wherever a rough-metal or rough-glass
surface faces the camera, with values far above 1 (up to ~96 in the attached
scene). An albedo AOV is expected to be a view-independent reflectance in
[0, 1] — this silently corrupts every denoising workflow built on it (e.g.
the OptiX denoiser integration from #283).
The cause is the base-class fallback of
BSDF::eval_diffuse_reflectance()(
evalatwo = (0, 0, 1)times π): exact for a Lambertian lobe, but forBSDFs without a diffuse lobe it evaluates the glossy microfacet lobe, which
spikes at the specular peak when the view direction approaches the normal.
Related precedent: #862 (missing override in
NormalMap).This PR overrides
eval_diffuse_reflectance()in the two microfacet-onlyplugins, following the Open Image Denoise albedo recommendations:
roughconductor→ the normal-incidence Fresnel reflectance (F0),modulated by
specular_reflectancewhen present (OIDN: "the reflectivityat normal incidence" for metallic surfaces).
roughdielectric→ 1, tinted byspecular_transmittancewhen present(OIDN: an albedo of 1 for non-delta dielectric surfaces such as glass).
The doxygen documentation in
include/mitsuba/render/bsdf.his updatedaccordingly. If a stricter reading of the "diffuse reflectance" contract is
preferred (return 0, since neither plugin has a diffuse lobe), the first
commit of this branch implements exactly that and the follow-up commits can
be dropped — happy to adjust.
Fixes # (issue)
Testing
scalar_rgb(and the full files passincluding the existing chi2/eval_pdf tests, 20/20):
test_rough_conductor.py::test07_eval_diffuse_reflectance— value equalsF0 computed from η, k (η=0.2, k=3 → 0.9234) at five view angles
(view-independence + correctness).
test_rough_dielectric.py::test14_eval_diffuse_reflectance— value equals1 for plain rough glass and equals the tint for
specular_transmittance = (0.2, 0.4, 0.6), at five view angles.roughconductor23.83 /
roughdielectric0.97 at cos θ = 0.999, view-dependent; after →flat F0 / 1.0 at all angles.
diffusecontrol unchanged at its reflectance.cuda_ad_rgb,aovintegratoralbedo:albedowrappingpath): Tungsten "spaceship" close-up of a rough-glass canopy and roughmetal. Before: albedo max 96.27 with a camera-tracking blob. After: glass
dome flat 1.0, metal flat F0·
specular_reflectance≈ 0.537, noview-dependent content, max value 1.0.
Checklist
cuda_*andllvm_*variants. If you can't test this, please leave below