Need help with sampleF()

Discussion related to the LuxRender Material system, programming API and Scene file format.

Moderators: jromang, tomb, zcott, coordinators

Need help with sampleF()

Hi all,

I'm getting some strange results with the layered material which I think are coming from my sampleF() function. Here's the current code :

Code: Select all
bool LayeredBSDF::SampleF(const SpectrumWavelengths &sw, const Vector &known, Vector *sampled,   float u1, float u2, float u3, SWCSpectrum *const f_, float *pdf,   BxDFType flags, BxDFType *sampledType, float *pdfBack,   bool reverse) const{   // Currently this checks to see if there is an interaction with the layers based on the opacity settings   // If there is no interaction - let the ray pass through and return a specular   // Otherwise returns a glossy (regardless of underlying layer types)   // Have two possible types to return - glossy or specular - see if either/both have been requested      bool glossy= (flags & BSDF_GLOSSY) ? true : false;   bool specular= (flags & BSDF_SPECULAR) ? true : false;   bool reflect= (flags & BSDF_REFLECTION) ? true : false;   bool transmit= (flags & BSDF_TRANSMISSION) ? true : false;   if (!reflect && !transmit) {      // new convention      reflect=true;      transmit=true;   }   *pdf = 1.0f;   if (pdfBack) {      *pdfBack = 1.0f;   }   if (glossy&&specular) { // then choose one      if (u1<prob_sample_spec) {         glossy=false;   // just do specular         *pdf *= prob_sample_spec;      }      else {         specular=false;   // just do glossy         *pdf *= (1.0f-prob_sample_spec);      }   }   if (reflect&&transmit) { // choose one      RandomGenerator rng(getRandSeed());      if (rng.floatValue()<0.5) {         reflect=false;}      else {         transmit=false;      }      *pdf *=0.5;   }   SWCSpectrum F(0.f);   float fwdpdf=1.0f;   float backpdf=1.0f;   if (glossy) { // then random sample hemisphere and return F() value            Sample_F_glossy(sw, known, sampled, &F, &fwdpdf, sampledType, &backpdf,         reverse,transmit,u2,u3);         }   if (specular) { // then random sample a specular path and return it      Sample_F_specular(sw, known, sampled, &F, &fwdpdf, sampledType, &backpdf,         reverse,transmit);   }   *f_=F / *pdf;         if (pdfBack) {      *pdfBack *= *pdf * backpdf;   }   *pdf *= fwdpdf;   return true;}

Sample_F_specular returns the F() for a randomly chosen path that has only specular interactions.

Sample_F_glossy returns the F() for every other type of path - but won't include purely specular paths. I'm calling any path that involves at least one diffuse or glossy interaction a glossy.

If both glossy and specular samples are valid then I choose one (based on prob_sample_spec) and adjust the pdfs . If I choose a specular then I sample a random specular path and return that. If I choose a glossy I just sample the hemisphere (uniform for now but might change to cos) and call F().

If I set prob_sample_spec=1.0f then I never sample the glossy component - so sampleF() will only return specular paths. The problem is that the diffuse still shows up - at about 50% of it's strength - presumably through a call to F().

I'm wondering if when I sample a specular I should also add in the F() for the glossy (if that's what was requested)? Ie, something like
Code: Select all
if (specular && !glossy) : sample specularif (!specular && glossy) : sample hemisphere and call F()if (specular && glossy) : choose specular or hemisphere to sample. if choose specular include glossy component for that direction

This doesn't seem to be how MultiBSDF works?
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

Hi,

That seems overly complicated and sometimes wrong, also please try to follow current coding style regarding spacing, naming and block alignment.

The flags parameter is what the user is requesting, if the user doesn't request BSDF_REFLECTION nor BSDF_TRANSMISSION, then you shouldn't return anything, the none means both rule only applies to BxDF flags (ie a BxDF defined to be only BSDF_DIFFUSE can be selected both for reflection and transmission by the BSDF).

You don't need to initialize pdf and pdfBack early since there's no early exit, and if there were an early exit path, you wouldn't need to have them initialized.

You don't take into account the nature of the layers when selecting the sampling mode, so you might select a specular sampling while there's no specular layer, or a transmition while the base or an intermediate layer is opaque. This is detrimental to the efficiency, but the worse thing here is that you'll always return true while you should return false in case the sampling is inappropriate.

Note that you could theoretically have a diffuse intermediate layer, this doesn't seem to be accounted for.

Initializing a random number generator is extremely costly, you could perfectly use the same random number to select glossy/specular and reflection/transmission (just rescale the random number so that the select area is expanded to 0-1). However common usage is that u3 selects the scattering mode and u1/u2 select the direction, you should probably also transmit the rescaled u3 to your helpers to sample the intermediate layers.

Your helpers take mostly the same arguments than SampleF but in a completely shuffled order, that hinders readability, they should also return true or false because you need to return something sensible in SampleF.

*f_ = F/ *pdf; is wrong because *pdf is subsequently modified, so you are completely missing the pdf of the layers interaction (unless your description is inaccurate and you already account for it).

Use const keywords to help the optimizer when your variable isn't modified, that'll also help spot mistakes in case you change the wrong variable.

Jeanphi
jeanphi

Posts: 6577
Joined: Mon Jan 14, 2008 7:21 am

Re: Need help with sampleF()

Hi Jeanphi, thanks for the reply.

Performance issues aside (this is of course highly experimental):

1) Is there a difference between returning an F of black with return true vs just returning false?

2) When the sampleF() flags are BSDF_ALL - and I sample a specular - should I include contributions from the glossy function?

*f_ = F/ *pdf; is wrong because *pdf is subsequently modified, so you are completely missing the pdf of the layers interaction (unless your description is inaccurate and you already account for it).

Yes, I think its already accounted for - the returned F is already /= fwdpdf by the definition of sampleF() so when i multiply the pdf I'm just bringing it into line
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

paco wrote:1) Is there a difference between returning an F of black with return true vs just returning false?

It might because pdf values might be uninitialized or unusable (NaN, negative, ...)

paco wrote:2) When the sampleF() flags are BSDF_ALL - and I sample a specular - should I include contributions from the glossy function?

When you sample a purely specular path, you should not include any other scattering event type. However you can include specular events in a path comprising a diffuse or glossy scattering event because the resulting path won't be specular anymore.

Jeanphi
jeanphi

Posts: 6577
Joined: Mon Jan 14, 2008 7:21 am

Re: Need help with sampleF()

Thanks again jeanphi

I guess I should clarify that the Sample_F_glossy function samples a paths through the layered mat using bidir - only paths with at least one non-specular component are included. The Sample_F_specular function only samples paths that have only specular components. In that way there is no overlap between the glossy/specular sample functions.

I wanted to check that my handling of the two different components was correct as the glossy part seems to be left out. Ie, I thought that by selecting either the glossy or specular part to sample and then compensating for that by modifying the pdf as in:

Code: Select all
if (glossy&&specular) { // then choose one      if (u1<prob_sample_spec) {         glossy=false;   // just do specular         *pdf *= prob_sample_spec;      }      else {         specular=false;   // just do glossy         *pdf *= (1.0f-prob_sample_spec);      }   }

Should mean that I get the same result regardless of pspec, although convergence will obviously differ. My problem is that modifying prob_sample_spec (=pspec) has a noticable effect on the brightness of the glossy component - ie if I set pspec=0 then I get no specular component and the glossy looks fine. If I set pspec =1.0 then I get a normal specular component and still get a glossy (but about 50%).

Seems as though the glossy component I get is (1.0 + (1.0- prob_sample_spec) ) / 2.0 - and it's that behaviour that I'm currently trying to figure out.

One way around this is to add in a glossy part when sampling specular - but this doesn't make any logical sense to me so was trying to find out if I've made a wrong assumption somewhere.
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

Ok, i've done some more testing and there is some flaw with my understanding of SampleF() that i'm hoping someone can spot.

I'd been basing my understanding on MultiBSDF, which I thought would have multiple BxDFs that were added together (not mix). I thought that the weights used in that were just used to decide a sampling strategy.

However if I put a glass BxDF and a lambertian BxDF into that, and change the weight in the SimpleSpecularReflection::Weight() function to return 1.0 - then I get the same behaviour as my layered sampleF() - ie, by increasing the weight of the glass there is a decreased contribution from the underlying diffuse BxDF. Ie, it's behaving more like a MixBSDF. For a standard glossy material this effect would be quite small and subtle - so probably won't show up in most scenes.

I guess my question is now : what is the correct way to sample a BSDF which has multiple independent components?
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

Hi jeanphi,

Thanks for the code cleanup - I was planning to do it once I nailed down the issue with sampleF.

Any ideas why MultiBSDF behaves the way it does (ie, seems to mix depending on weights) ?
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

Hi,

Why do you say it mixes based on Weight? It doesn't, the weight is just used to select the sampled BxDF, but the returned F value doesn't depend on the weight.

Jeanphi
jeanphi

Posts: 6577
Joined: Mon Jan 14, 2008 7:21 am

Re: Need help with sampleF()

It's just that if you adjust the weights you get a different brightness in the final scene - I though the weights were just to help select an optimal sampling strategy and they would affect convergence speed, but not the final result?
paco

Posts: 456
Joined: Sun Feb 07, 2010 1:37 am

Re: Need help with sampleF()

Hi,

Then it's probably a bug, do you have an example so that I can investigate?

Jeanphi
jeanphi

Posts: 6577
Joined: Mon Jan 14, 2008 7:21 am

Next