9.4 Conductor BRDF

Having described the relevant physical principles, we now turn to the implementation of a BRDF that models specular reflection from an interface between a dielectric (e.g., air or water) and a conductor (e.g., a polished metal surface). We initially focus on the smooth case, and later generalize the implementation to rough interfaces in Section 9.6.

<<ConductorBxDF Definition>>= 
class ConductorBxDF { public: <<ConductorBxDF Public Methods>> 
ConductorBxDF(const TrowbridgeReitzDistribution &mfDistrib, SampledSpectrum eta, SampledSpectrum k) : mfDistrib(mfDistrib), eta(eta), k(k) {} BxDFFlags Flags() const { return mfDistrib.EffectivelySmooth() ? BxDFFlags::SpecularReflection : BxDFFlags::GlossyReflection; } pstd::optional<BSDFSample> Sample_f(Vector3f wo, Float uc, Point2f u, TransportMode mode, BxDFReflTransFlags sampleFlags = BxDFReflTransFlags::All) const { if (!(sampleFlags & BxDFReflTransFlags::Reflection)) return {}; if (mfDistrib.EffectivelySmooth()) { <<Sample perfect specular conductor BRDF>> 
Vector3f wi(-wo.x, -wo.y, wo.z); SampledSpectrum f = FrComplex(AbsCosTheta(wi), eta, k) / AbsCosTheta(wi); return BSDFSample(f, wi, 1, BxDFFlags::SpecularReflection);
} <<Sample rough conductor BRDF>> 
<<Sample microfacet normal omega Subscript normal m and reflected direction omega Subscript normal i >> 
Vector3f wm = mfDistrib.Sample_wm(wo, u); Vector3f wi = Reflect(wo, wm); if (!SameHemisphere(wo, wi)) return {};
<<Compute PDF of wi for microfacet reflection>> 
Float pdf = mfDistrib.PDF(wo, wm) / (4 * AbsDot(wo, wm));
Float cosTheta_o = AbsCosTheta(wo), cosTheta_i = AbsCosTheta(wi); <<Evaluate Fresnel factor F for conductor BRDF>> 
SampledSpectrum F = FrComplex(AbsDot(wo, wm), eta, k);
SampledSpectrum f = mfDistrib.D(wm) * F * mfDistrib.G(wo, wi) / (4 * cosTheta_i * cosTheta_o); return BSDFSample(f, wi, pdf, BxDFFlags::GlossyReflection);
} SampledSpectrum f(Vector3f wo, Vector3f wi, TransportMode mode) const { if (!SameHemisphere(wo, wi)) return {}; if (mfDistrib.EffectivelySmooth()) return {}; <<Evaluate rough conductor BRDF>> 
<<Compute cosines and omega Subscript normal m for conductor BRDF>> 
Float cosTheta_o = AbsCosTheta(wo), cosTheta_i = AbsCosTheta(wi); if (cosTheta_i == 0 || cosTheta_o == 0) return {}; Vector3f wm = wi + wo; if (LengthSquared(wm) == 0) return {}; wm = Normalize(wm);
<<Evaluate Fresnel factor F for conductor BRDF>> 
SampledSpectrum F = FrComplex(AbsDot(wo, wm), eta, k);
return mfDistrib.D(wm) * F * mfDistrib.G(wo, wi) / (4 * cosTheta_i * cosTheta_o);
} Float PDF(Vector3f wo, Vector3f wi, TransportMode mode, BxDFReflTransFlags sampleFlags) const { if (!(sampleFlags & BxDFReflTransFlags::Reflection)) return 0; if (!SameHemisphere(wo, wi)) return 0; if (mfDistrib.EffectivelySmooth()) return 0; <<Evaluate sampling PDF of rough conductor BRDF>> 
Vector3f wm = wo + wi; if (LengthSquared(wm) == 0) return 0; wm = FaceForward(Normalize(wm), Normal3f(0, 0, 1)); return mfDistrib.PDF(wo, wm) / (4 * AbsDot(wo, wm));
} PBRT_CPU_GPU static constexpr const char *Name() { return "ConductorBxDF"; } std::string ToString() const; void Regularize() { mfDistrib.Regularize(); }
private: <<ConductorBxDF Private Members>>  };

The internal state of the ConductorBxDF consists of the real (eta) and imaginary (k) component of the index of refraction. Furthermore, the implementation requires a microfacet distribution that statistically describes its roughness. The TrowbridgeReitzDistribution class handles the details here. The constructor, not included here, takes these fields as input and stores them in the ConductorBxDF instance.

<<ConductorBxDF Private Members>>= 

We will sidestep all discussion of microfacets for the time being and only cover the effectively smooth case in this section, where the surface is either perfectly smooth or so close to it that it can be modeled as such. The TrowbridgeReitzDistribution provides an EffectivelySmooth() method that indicates this case, in which the microfacet distribution plays no further role. The ConductorBxDF::Flags() method returns BxDFFlags accordingly.

<<ConductorBxDF Public Methods>>= 
BxDFFlags Flags() const { return mfDistrib.EffectivelySmooth() ? BxDFFlags::SpecularReflection : BxDFFlags::GlossyReflection; }

The conductor BRDF builds on two physical ideas: the law of specular reflection assigns a specific reflected direction to each ray, and the Fresnel equations determine the portion of reflected light. Any remaining light refracts into the conductor, where it is rapidly absorbed and converted into heat.

Let upper F Subscript normal r Baseline left-parenthesis omega Subscript Baseline right-parenthesis denote the unpolarized Fresnel reflectance of a given direction omega Subscript (which only depends on the angle theta that this direction makes with the surface normal bold n Subscript ). Because the law of specular reflection states that theta Subscript normal r Baseline equals theta Subscript normal o , we have upper F Subscript normal r Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis equals upper F Subscript normal r Baseline left-parenthesis omega Subscript normal o Baseline right-parenthesis . We thus require a BRDF f Subscript normal r such that

upper L Subscript normal o Superscript Baseline left-parenthesis omega Subscript normal o Baseline right-parenthesis equals integral Underscript script upper H squared left-parenthesis bold n Subscript Baseline right-parenthesis Endscripts f Subscript normal r Baseline left-parenthesis omega Subscript normal o Baseline comma omega Subscript normal i Baseline right-parenthesis upper L Subscript normal i Superscript Baseline left-parenthesis omega Subscript normal i Baseline right-parenthesis StartAbsoluteValue cosine theta Subscript normal i Baseline EndAbsoluteValue normal d omega Subscript normal i Baseline equals upper F Subscript normal r Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis upper L Subscript normal i Superscript Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis comma

where omega Subscript normal r Baseline equals normal upper R left-parenthesis omega Subscript normal o Baseline comma bold n Subscript Baseline right-parenthesis is the specular reflection vector for omega Subscript normal o reflected about the surface normal  bold n Subscript . Such a BRDF can be constructed using the Dirac delta distribution that represents an infinitely peaked signal. Recall from Section 8.1 that the delta distribution has the useful property that

integral f left-parenthesis x right-parenthesis delta left-parenthesis x minus x 0 right-parenthesis normal d x equals f left-parenthesis x 0 right-parenthesis period
(9.8)

A first guess might be to use delta functions to restrict the incident direction to the specular reflection direction omega Subscript normal r . This would yield a BRDF of

f Subscript normal r Baseline left-parenthesis omega Subscript normal o Baseline comma omega Subscript normal i Baseline right-parenthesis equals delta left-parenthesis omega Subscript normal i Baseline minus omega Subscript normal r Baseline right-parenthesis upper F Subscript normal r Baseline left-parenthesis omega Subscript normal i Baseline right-parenthesis period

Although this seems appealing, plugging it into the scattering equation, Equation (4.14), reveals a problem:

StartLayout 1st Row 1st Column upper L Subscript normal o Baseline left-parenthesis omega Subscript normal o Baseline right-parenthesis 2nd Column equals integral Underscript script upper H squared left-parenthesis bold n Subscript Baseline right-parenthesis Endscripts delta left-parenthesis omega Subscript normal i Baseline minus omega Subscript normal r Baseline right-parenthesis upper F Subscript normal r Baseline left-parenthesis omega Subscript normal i Baseline right-parenthesis upper L Subscript normal i Baseline left-parenthesis omega Subscript normal i Baseline right-parenthesis StartAbsoluteValue cosine theta Subscript normal i Baseline EndAbsoluteValue normal d omega Subscript normal i Baseline 2nd Row 1st Column Blank 2nd Column equals upper F Subscript normal r Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis upper L Subscript normal i Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis StartAbsoluteValue cosine theta Subscript normal r Baseline EndAbsoluteValue period EndLayout

This is not correct because it contains an extra factor of cosine theta Subscript normal r . However, we can divide out this factor to find the correct BRDF for perfect specular reflection:

f Subscript normal r Baseline left-parenthesis normal p Subscript Baseline comma omega Subscript normal o Baseline comma omega Subscript normal i Baseline right-parenthesis equals upper F Subscript normal r Baseline left-parenthesis omega Subscript normal r Baseline right-parenthesis StartFraction delta left-parenthesis omega Subscript normal i Baseline minus omega Subscript normal r Baseline right-parenthesis Over StartAbsoluteValue cosine theta Subscript normal r Baseline EndAbsoluteValue EndFraction period
(9.9)

The Sample_f() method of the ConductorBxDF method implements Equation (9.9).

<<ConductorBxDF Public Methods>>+=  
pstd::optional<BSDFSample> Sample_f(Vector3f wo, Float uc, Point2f u, TransportMode mode, BxDFReflTransFlags sampleFlags = BxDFReflTransFlags::All) const { if (!(sampleFlags & BxDFReflTransFlags::Reflection)) return {}; if (mfDistrib.EffectivelySmooth()) { <<Sample perfect specular conductor BRDF>> 
Vector3f wi(-wo.x, -wo.y, wo.z); SampledSpectrum f = FrComplex(AbsCosTheta(wi), eta, k) / AbsCosTheta(wi); return BSDFSample(f, wi, 1, BxDFFlags::SpecularReflection);
} <<Sample rough conductor BRDF>> 
<<Sample microfacet normal omega Subscript normal m and reflected direction omega Subscript normal i >> 
Vector3f wm = mfDistrib.Sample_wm(wo, u); Vector3f wi = Reflect(wo, wm); if (!SameHemisphere(wo, wi)) return {};
<<Compute PDF of wi for microfacet reflection>> 
Float pdf = mfDistrib.PDF(wo, wm) / (4 * AbsDot(wo, wm));
Float cosTheta_o = AbsCosTheta(wo), cosTheta_i = AbsCosTheta(wi); <<Evaluate Fresnel factor F for conductor BRDF>> 
SampledSpectrum F = FrComplex(AbsDot(wo, wm), eta, k);
SampledSpectrum f = mfDistrib.D(wm) * F * mfDistrib.G(wo, wi) / (4 * cosTheta_i * cosTheta_o); return BSDFSample(f, wi, pdf, BxDFFlags::GlossyReflection);
}

Note that Dirac delta distributions require special handling compared to standard functions. In particular, the probability of successfully drawing a point on the peak is zero, unless the sampling probability is also a delta distribution. In other words, the distribution must be used to determine the sample location.

Because the surface normal bold n Subscript normal g is left-parenthesis 0 comma 0 comma 1 right-parenthesis in the reflection coordinate system, the equation for the perfect specular reflection direction, (9.1), simplifies substantially; the x and y components only need to be negated to compute this direction and there is no need to call Reflect() (the rough case will require this function, however).

<<Sample perfect specular conductor BRDF>>= 
Vector3f wi(-wo.x, -wo.y, wo.z); SampledSpectrum f = FrComplex(AbsCosTheta(wi), eta, k) / AbsCosTheta(wi); return BSDFSample(f, wi, 1, BxDFFlags::SpecularReflection);

The PDF value in the returned BSDFSample is set to one, as per the discussion of delta distribution BSDFs in Section 9.1.2. Following the other conventions outlined in that section, BRDF evaluation always returns zero in the smooth case, since the specular peak is considered unreachable by other sampling methods.

<<ConductorBxDF Public Methods>>+=  
SampledSpectrum f(Vector3f wo, Vector3f wi, TransportMode mode) const { if (!SameHemisphere(wo, wi)) return {}; if (mfDistrib.EffectivelySmooth()) return {}; <<Evaluate rough conductor BRDF>> 
<<Compute cosines and omega Subscript normal m for conductor BRDF>> 
Float cosTheta_o = AbsCosTheta(wo), cosTheta_i = AbsCosTheta(wi); if (cosTheta_i == 0 || cosTheta_o == 0) return {}; Vector3f wm = wi + wo; if (LengthSquared(wm) == 0) return {}; wm = Normalize(wm);
<<Evaluate Fresnel factor F for conductor BRDF>> 
SampledSpectrum F = FrComplex(AbsDot(wo, wm), eta, k);
return mfDistrib.D(wm) * F * mfDistrib.G(wo, wi) / (4 * cosTheta_i * cosTheta_o);
}

The same convention also applies to the PDF() method.

<<ConductorBxDF Public Methods>>+= 
Float PDF(Vector3f wo, Vector3f wi, TransportMode mode, BxDFReflTransFlags sampleFlags) const { if (!(sampleFlags & BxDFReflTransFlags::Reflection)) return 0; if (!SameHemisphere(wo, wi)) return 0; if (mfDistrib.EffectivelySmooth()) return 0; <<Evaluate sampling PDF of rough conductor BRDF>> 
Vector3f wm = wo + wi; if (LengthSquared(wm) == 0) return 0; wm = FaceForward(Normalize(wm), Normal3f(0, 0, 1)); return mfDistrib.PDF(wo, wm) / (4 * AbsDot(wo, wm));
}

The missing three fragments—<<Sample rough conductor BRDF>>, <<Evaluate rough conductor BRDF>>, and <<Evaluate sampling PDF of rough conductor BRDF>>—will be presented in Section 9.6.