mmp / pbrt-v3

Source code for pbrt, the renderer described in the third edition of "Physically Based Rendering: From Theory To Implementation", by Matt Pharr, Wenzel Jakob, and Greg Humphreys.
http://pbrt.org
BSD 2-Clause "Simplified" License
4.86k stars 1.18k forks source link

Disney BSDF #313

Open efylmzr opened 2 years ago

efylmzr commented 2 years ago

Hi, I am currently implementing the Disney BSDF in Mitsuba as a project, and I was using PBRT as my baseline implementation. Apparently PBRT has some issues, especially in transmission and thin sheet approximation. I will try to cover them all. Also there are some indetermined issues which are not explained well (or not explained at all) in Disney course notes. I saw in the comments of the code that you communicated with Brent Burley, maybe you know the correct answers.

Schlick Approximation

This is a small mistake, the ordering in lerp() function is wrong in Fr_Schlick() function. It should be return Lerp(R0,SchlickWeight(cosTheta), 1);. instead of return Lerp(SchlickWeight(cosTheta), R0, 1);

Also this Schlick approximation is wrong if eta<1. It should be adjusted in case th rendering is done under water :).

BSDF Lobe

The rates for some lobes are different from what is explained in the paper. For example, the rate for Microfacet Transmission should be (1-metallic)*spec_trans . here is a rendering with metallic=1, spec_trans=1 : scene_metallic1 The reason for the bad image is that transmission lobe is evaluated which should not be when metallic parameter is 1. I created a flow chart for my report which can be useful. disney

Also sheen is not present in BSDF lobe (in PBRT implementation) which is explicitly mentioned in the course notes.

Well, from what I have understood from the 2015 Disney course notes, when all of the other lobes are turned of except BSDF , we should get the same thing as rough dielectric implementation with GGX distribution. When I render that configuration in PBRT, I get the following:

scene_redglass When I render the same scene with rough dielectric : scene_dielectric

PBRT uniformly selects one of the active lobes while sampling so the convergence is pretty bad . But the main problem here is not taking care of the total internal reflection cases. Every time a total internal reflection occurs, half of the samples (in this configuration since only Microfacet reflection and transmission is turned on) are wasted, and paths can not reach anywhere for some points. After taking care of that issue in Mitsuba, I got the following (again equal probability between lobes) : scene_disney_s1

I also blended the brdf and bsdf lobe (metallic=0, spec_trans = 0 to 1) and created some renderings. With PBRT, I got this : blend_pbrt

I was also getting the same thing with my implementation. The problem here is sampling and evaluating all of the lobes when the light is inside. (wi.z() < 0). Evaluating diffuse or retroreflection for internal reflection case did not seem right to me.(Course notes does not explain anything about this.) The most intuitive thing to do was only turning on BSDF minor lobes (MF Reflection, MF Transmission, Clearcoat and Sheen (I did not sample sheen though, just evaluated) ) when the light is inside. So when I tried that, I got the following in Mitsuba: (with importance sampling based on material parameters.): diffuse_blend_s1 The I used Fresnel coefficients while sampling. I got this: blend_diffuse_fresnel Complete glass configuration was even better (it actually becomes same as the rough dielectric implementation in glass configuration): scene_disney_s2

Everything was converging quiet faster, I was getting nice looking images and it was passing most of the chi2 tests. The life was good :) until I did a rendering of a glass with clearcoat and sheen : scene_sheenclearcoat_fresnel I do not know what is hapening here, but when I use clearcoat and sheen components for inside, everywhere is full of fireflies. (Sheen is understandable, I do not sample it.) Maybe sampling 2 different specular lobe for inside is too complicated due to internal reflections etc. In the end, I discarded sheen and clearcoat for inside case ...

Thin Model

There are only two paragraphs related to the thin model. So I am not sure about this part also but it seems that the paper is talking about a different model than the main BSDF. One thing they say in the paper is that spec_trans parameter fully blends between a fully diffuse model and a fully specular model. PBRT implementation is different from this info. When I render a thin sheet with PBRT with spec_trans = 0, I get this: scene_spectrans0_thin which still has specular parts. It uses the main BSDF model lobe coefficients. I think, the coefficients should be like this: thin_disney

There is no mention to the clearcoat and metallic parts so I just excluded them. When I render thin sheets with spec_trans = 0 .. 1, I get this with my implementation: blend_spec_thin

Another issue is that PBRT uses normal Microfacet Transmission implementation which is not the case. This creates bending when we are going through the thin sheet. For a thin glass, such effect is not present. Instead of that, MicrofacetReflection should be sampled and inverted to the back side of the sheet. And instead of asymmetric BRDF function, (1-F) D G / (4cos(i)cos(o)) must be used. To see the effect of bending, check this rendering (PBRT):

scene_thin_glass

Also, instead of multiplying the square root of the color, we should multiply the true color value in bsdf evaluation when refracting. (Sqrt is for 3D case. After 2 refractions, true color value is multiplied. )

These are all the potential problems that I see. If you think that there is something wrong above, please share info.

efylmzr commented 2 years ago

Disney Fresnel

Lately, I observed another problem related to Disney Fresnel Function while creating images for my report. See the following: (do not mind flatness, the images are wrong there.): normal_blend_old

If you look at the spec_tint parameter, you can see that there is no change in the images as the parameter changes. It is not related to the lighting or selected parameters but implementation (I was using the same implementation with PBRT). In Disney 2012 course notes, it is stated that schlick approximation is used for all specular lobes. It is calculated as: F_schlick = F0 + (1-F0) (1- cos(i)^5) They calculated F0 as follows: F0 = metallic base_color + (1-metallic) F0_dielectric_eta ( (1-spec_tint) + spec_tint c_tint) where c_tint = color / luminosity and F0_dielectric_eta is calculated with eta. So for metallic part of F0, base_color is directly used. For dielectric part, spec_tint amount of F0_dielectric is shifted by c_tint parameter which is a normalized color.

Everything is fine so far. In 2015 course notes they decided to use the correct Fresnel implementation for dielectric part where as specular_tint and metallic is still approximated by Schlick. But how they did it is ambiguous. (It is not clear how to do that because they blended F0 parameter in 2012 where true implementation of F_dielectric gives the result for all angles so can not be blended with F0). For this problem, PBRT applies, F = F_true_dielectric (1-metallic) + metallic F_schlick where F_schlick is the same value given above. This is nonsense since F_schlick above also takes into account dielectric part. So when metallic=0, we can not see any tint in specular dielectric.

Since they said that specular tint is also evaluated with schlick approximation, I applied: F =F_true_dielectric (1-metallic) (1-spec_tint) + F_schlick_tint (1-metallic) spec_tint + metallic F_schlick_metallic where F_schlick_metallic = color + (1-color) (1- cos(i)^5) F_schlick_tint = F0_dielectric_eta c_tint + (1-F0_dielectric_eta c_tint ) * (1- cos(i)^5) I am just applying schlick approximation twice (for specular tinted dielectric part and metallic part.) So if spec_tint is given as 1, true value of Fresnel function is not used at all. After applying this, I got the following: normal_blend So this result is more correct, bot I do not know the true implementation.

Clearcoat Masking Function

Clearcoat masking function does not match the correct formula.

PotatoPot6676 commented 2 years ago

@efylmzr : Not sure if this would help but RenderMan examples come with the source code for the Disney BSDF (PxrDisneyBsdf.cpp) and the code is generously commented. https://rmanwiki.pixar.com/display/REN24/PxrDisneyBsdf

Perhaps you might find it a useful reference to see how they have implemented it in their production renderer.

gracien-app commented 1 year ago

@efylmzr : Not sure if this would help but RenderMan examples come with the source code for the Disney BSDF (PxrDisneyBsdf.cpp) and the code is generously commented. https://rmanwiki.pixar.com/display/REN24/PxrDisneyBsdf

Perhaps you might find it a useful reference to see how they have implemented it in their production renderer.

@PotatoPot6676 Would you mind sharing where this source code is available as of today? I can't manage to find it - neither on the linked website, nor PxrDisney Fridge example attached there.

Thank you.

PotatoPot6676 commented 1 year ago

@gracien-app Pixar provides the source code for some of their BSDF and Integrator plugins as part of the examples which can be downloaded from the RenderMan installer (You would need to register for an NCR license first).

image

I'm unsure of the licensing that applies to the source code so you might want to steer clear of it if its not permissive.

Also, Mitsuba 3 just released and @efylmzr's contributions are included in it. It might make more sense to check that repo instead as it pertains to the discussions in this thread: https://github.com/mitsuba-renderer/mitsuba3

gracien-app commented 1 year ago

@PotatoPot6676 Thank you so much. I wasn't aware Mitsuba 3 was released, and it seems that the Principled BSDF is the visual reference I was looking for.

Sincerely, Gracien.