## 8.2 Specular Reflection and Transmission

The behavior of light at perfectly smooth surfaces is relatively easy to characterize analytically using both the physical and geometric optics models. These surfaces exhibit perfect specular reflection and transmission of incident light; for a given direction, all light is scattered in a single outgoing direction . For specular reflection, this direction is the outgoing direction that makes the same angle with the normal as the incoming direction:

and where . For transmission, we again have , and the outgoing direction is given by Snell’s law, which relates the angle between the transmitted direction and the surface normal to the angle between the incident ray and the surface normal . (One of the exercises at the end of this chapter is to derive Snell’s law using Fermat’s principle from optics.) Snell’s law is based on the index of refraction for the medium that the incident ray is in and the index of refraction for the medium it is entering. The index of refraction describes how much more slowly light travels in a particular medium than in a vacuum. We will use the Greek letter , pronounced “eta,” to denote the index of refraction. Snell’s law is

(8.2)

In general, the index of refraction varies with the wavelength of light. Thus, incident light generally scatters in multiple directions at the boundary between two different media, an effect known as dispersion. This effect can be seen when incident white light is split into spectral components by a prism. Common practice in graphics is to ignore this wavelength dependence, since this effect is generally not crucial for visual accuracy and ignoring it simplifies light transport calculations substantially. Alternatively, the paths of multiple beams of light (e.g., at a series of discrete wavelengths) can be tracked through the environment in which a dispersive object is found. The “Further Reading” section at the end of Chapter 14 has pointers to more information on this topic.

Figure 8.4 shows the effect of perfect specular reflection and transmission.

### 8.2.1 Fresnel Reflectance

In addition to the reflected and transmitted directions, it is also necessary to compute the fraction of incoming light that is reflected or transmitted. For physically accurate reflection or refraction, these terms are directionally dependent and cannot be captured by constant per-surface scaling amounts. The Fresnel equations describe the amount of light reflected from a surface; they are the solution to Maxwell’s equations at smooth surfaces.

Given the index of refraction and the angle which the incident ray makes with the surface normal, the Fresnel equations specify the material’s corresponding reflectance for two different polarization states of the incident illumination. Because the visual effect of polarization is limited in most environments, in pbrt we will make the common assumption that light is unpolarized; that is, it is randomly oriented with respect to the light wave. With this simplifying assumption, the Fresnel reflectance is the average of the squares of the parallel and perpendicular polarization terms.

At this point, it is necessary to draw a distinction among several important classes of materials:

1. The first class is dielectrics, which are materials that don’t conduct electricity. They have real-valued indices of refraction (usually in the range 1-3) and transmit a portion of the incident illumination. Examples of dielectrics are glass, mineral oil, water, and air.
2. The second class consists of conductors such as metals. Valence electrons can freely move within the their atomic lattice, allowing electric currents to flow from one place to another. This fundamental atomic property translates into a profoundly different behavior when a conductor is subjected to electromagnetic radiation such as visible light: the material is opaque and reflects back a significant portion of the illumination. A portion of the light is also transmitted into the interior of the conductor, where it is rapidly absorbed: total absorption typically occurs within the top 0.1 m of the material, hence only extremely thin metal films are capable of transmitting appreciable amounts of light. We ignore this effect in pbrt and only model the reflection component of conductors. In contrast to dielectrics, conductors have a complex-valued index of refraction .
3. Semiconductors such as silicon or germanium are the third class though we will not consider them in this book.

Both conductors and dielectrics are governed by the same set of Fresnel equations. Despite this, we prefer to create a special evaluation function for dielectrics to benefit from the particularly simple form that these equations take on when the indices of refraction are guaranteed to be real-valued.

Medium Index of refraction
Vacuum 1.0
Air at sea level 1.00029
Ice 1.31
Water (C) 1.333
Fused quartz 1.46
Glass 1.5–1.6
Sapphire 1.77
Diamond 2.42

To compute the Fresnel reflectance at the interface of two dielectric media, we need to know the indices of refraction for the two media. Table 8.1 has the indices of refraction for a number of dielectric materials. The Fresnel reflectance formulae for dielectrics are

where is the Fresnel reflectance for parallel polarized light and is the reflectance for perpendicular polarized light, and are the indices of refraction for the incident and transmitted media, and and are the incident and transmitted directions. can be computed with Snell’s law (see Section 8.2.3).

The cosine terms should all be greater than or equal to zero; for the purposes of computing these values, the geometric normal should be flipped to be on the same side as and when computing and , respectively.

For unpolarized light, the Fresnel reflectance is

Due to conservation of energy, the energy transmitted by a dielectric is .

The function FrDielectric() computes the Fresnel reflection formula for dielectric materials and unpolarized light. The quantity is passed in with the parameter cosThetaI.

<<BxDF Utility Functions>>=
Float FrDielectric(Float cosThetaI, Float etaI, Float etaT) { cosThetaI = Clamp(cosThetaI, -1, 1); <<Potentially swap indices of refraction>>
bool entering = cosThetaI > 0.f; if (!entering) { std::swap(etaI, etaT); cosThetaI = std::abs(cosThetaI); }
<<Compute cosThetaT using Snell’s law>>
Float sinThetaI = std::sqrt(std::max((Float)0, 1 - cosThetaI * cosThetaI)); Float sinThetaT = etaI / etaT * sinThetaI; <<Handle total internal reflection>>
if (sinThetaT >= 1) return 1;
Float cosThetaT = std::sqrt(std::max((Float)0, 1 - sinThetaT * sinThetaT));
Float Rparl = ((etaT * cosThetaI) - (etaI * cosThetaT)) / ((etaT * cosThetaI) + (etaI * cosThetaT)); Float Rperp = ((etaI * cosThetaI) - (etaT * cosThetaT)) / ((etaI * cosThetaI) + (etaT * cosThetaT)); return (Rparl * Rparl + Rperp * Rperp) / 2; }

To find the cosine of the transmitted angle, cosThetaT, it is first necessary to determine if the incident direction is on the outside of the medium or inside it, so that the two indices of refraction can be interpreted appropriately.

The sign of the cosine of the incident angle indicates on which side of the medium the incident ray lies (Figure 8.5). If the cosine is between and , the ray is on the outside, and if the cosine is between and , the ray is on the inside. The parameters etaI and etaT are adjusted such that etaI has the index of refraction of the incident medium, and thus it is ensured that cosThetaI is nonnegative.

<<Potentially swap indices of refraction>>=
bool entering = cosThetaI > 0.f; if (!entering) { std::swap(etaI, etaT); cosThetaI = std::abs(cosThetaI); }

Once the indices of refraction are determined, we can compute the sine of the angle between the transmitted direction and the surface normal, , using Snell’s law (Equation (8.2)). Finally, the cosine of this angle is found using the identity .

<<Compute cosThetaT using Snell’s law>>=
Float sinThetaI = std::sqrt(std::max((Float)0, 1 - cosThetaI * cosThetaI)); Float sinThetaT = etaI / etaT * sinThetaI; <<Handle total internal reflection>>
if (sinThetaT >= 1) return 1;
Float cosThetaT = std::sqrt(std::max((Float)0, 1 - sinThetaT * sinThetaT));

When light is traveling from one medium to another medium with a lower index of refraction, none of the light at incident angles near grazing passes into the other medium. The largest angle at which this happens is called the critical angle; when is greater than the critical angle, total internal reflection occurs, and all of the light is reflected. That case is detected here by a value of greater than one; in that case, the Fresnel equations are unnecessary.

<<Handle total internal reflection>>=
if (sinThetaT >= 1) return 1;

We now focus on the general case of a complex index of refraction , where some of the incident light is potentially absorbed by the material and turned into heat. In addition to the real part, the general Fresnel formula now also depends on the imaginary part that is referred to as the absorption coefficient.

Figure 8.6 shows a plot of the index of refraction and absorption coefficient for gold; both of these are wavelength-dependent quantities. The directory scenes/spds/metals in the pbrt distribution has wavelength-dependent data for and for a variety of metals. Figure 9.4 in the next chapter shows a model rendered with a metal material.

The Fresnel reflectance at the boundary between a conductor and a dielectric medium is given by

(8.3)

where

and is the relative index of refraction computed using a complex division operation. However, generally will be a dielectric so that a normal real division can be used instead.

This computation is implemented by the FrConductor() function ; its implementation corresponds directly to Equation (8.3) and so isn’t included here.

<<Reflection Declarations>>=
Spectrum FrConductor(Float cosThetaI, const Spectrum &etaI, const Spectrum &etaT, const Spectrum &k);

For convenience, we will define an abstract Fresnel class that provides an interface for computing Fresnel reflection coefficients. Using implementations of this interface helps simplify the implementation of subsequent BRDFs that may need to support both forms.

<<BxDF Declarations>>+=
class Fresnel { public: <<Fresnel Interface>>
virtual ~Fresnel(); virtual Spectrum Evaluate(Float cosI) const = 0;
};

The only method provided by the Fresnel interface is Fresnel::Evaluate(). Given the cosine of the angle made by the incoming direction and the surface normal, it returns the amount of light reflected by the surface.

<<Fresnel Interface>>=
virtual Spectrum Evaluate(Float cosI) const = 0;

#### Fresnel Conductors

FresnelConductor implements this interface for conductors.

<<BxDF Declarations>>+=
class FresnelConductor : public Fresnel { public: <<FresnelConductor Public Methods>>
Spectrum Evaluate(Float cosThetaI) const; FresnelConductor(const Spectrum &etaI, const Spectrum &etaT, const Spectrum &k) : etaI(etaI), etaT(etaT), k(k) { }
private: Spectrum etaI, etaT, k; };

Its constructor stores the given index of refraction and absorption coefficient .

<<FresnelConductor Public Methods>>=
FresnelConductor(const Spectrum &etaI, const Spectrum &etaT, const Spectrum &k) : etaI(etaI), etaT(etaT), k(k) { }

The evaluation routine for FresnelConductor is also simple; it just calls the FrConductor() function defined earlier. Note that it takes the absolute value of cosThetaI before calling FrConductor(), since FrConductor() expects that the cosine will be measured with respect to the normal on the same side of the surface as , or, equivalently, that the absolute value of should be used.

<<BxDF Method Definitions>>+=
Spectrum FresnelConductor::Evaluate(Float cosThetaI) const { return FrConductor(std::abs(cosThetaI), etaI, etaT, k); }

#### Fresnel Dielectrics

FresnelDielectric similarly implements the Fresnel interface for dielectric materials.

<<BxDF Declarations>>+=
class FresnelDielectric : public Fresnel { public: <<FresnelDielectric Public Methods>>
Spectrum Evaluate(Float cosThetaI) const; FresnelDielectric(Float etaI, Float etaT) : etaI(etaI), etaT(etaT) { }
private: Float etaI, etaT; };

Its constructor stores the indices of refraction on the exterior and interior sides of the surface.

<<FresnelDielectric Public Methods>>=
FresnelDielectric(Float etaI, Float etaT) : etaI(etaI), etaT(etaT) { }

The evaluation routine for FresnelDielectric analogously calls FrDielectric().

<<BxDF Method Definitions>>+=
Spectrum FresnelDielectric::Evaluate(Float cosThetaI) const { return FrDielectric(cosThetaI, etaI, etaT); }

#### A Special Fresnel Interface

The FresnelNoOp implementation of the Fresnel interface returns 100% reflection for all incoming directions. Although this is physically implausible, it is a convenient capability to have available.

<<BxDF Declarations>>+=
class FresnelNoOp : public Fresnel { public: Spectrum Evaluate(Float) const { return Spectrum(1.); } };

### 8.2.2 Specular Reflection

We can now implement the SpecularReflection class, which describes physically plausible specular reflection, using the Fresnel interface to compute the fraction of light that is reflected. First, we will derive the BRDF that describes specular reflection. Since the Fresnel equations give the fraction of light reflected, , then we need a BRDF such that

where is the specular reflection vector for reflected about the surface normal . (Recall that for specular reflection, and therefore .)

Such a BRDF can be constructed using the Dirac delta distribution. Recall from Section 7.1 that the delta distribution has the useful property that

(8.4)

The delta distribution requires special handling compared to standard functions, however. In particular, numerical integration of integrals with delta distributions must explicitly account for the delta distribution. Consider the integral in Equation (8.4): if we tried to evaluate it using the trapezoid rule or some other numerical integration technique, by definition of the delta distribution there would be zero probability that any of the evaluation points would have a nonzero value of . Rather, we must allow the delta distribution to determine the evaluation point itself. We will encounter delta distributions in light transport integrals both from specular BxDFs as well as from some of the light sources in Chapter 12.

Intuitively, we want the specular reflection BRDF to be zero everywhere except at the perfect reflection direction, which suggests the use of the delta distribution. A first guess might be to use delta functions to restrict the incident direction to the specular reflection direction . This would yield a BRDF of

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

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

<<BxDF Declarations>>+=
class SpecularReflection : public BxDF { public: <<SpecularReflection Public Methods>>
SpecularReflection(const Spectrum &R, Fresnel *fresnel) : BxDF(BxDFType(BSDF_REFLECTION | BSDF_SPECULAR)), R(R), fresnel(fresnel) { } Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); } Spectrum Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const; Float Pdf(const Vector3f &wo, const Vector3f &wi) const { return 0; }
private: <<SpecularReflection Private Data>>
const Spectrum R; const Fresnel *fresnel;
};

The SpecularReflection constructor takes a Spectrum that is used to scale the reflected color and a Fresnel object pointer that describes dielectric or conductor Fresnel properties.

<<SpecularReflection Public Methods>>=
SpecularReflection(const Spectrum &R, Fresnel *fresnel) : BxDF(BxDFType(BSDF_REFLECTION | BSDF_SPECULAR)), R(R), fresnel(fresnel) { }

<<SpecularReflection Private Data>>=
const Spectrum R; const Fresnel *fresnel;

The rest of the implementation is straightforward. No scattering is returned from SpecularReflection::f(), since for an arbitrary pair of directions the delta function returns no scattering.

<<SpecularReflection Public Methods>>+=
Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); }

However, we do implement the Sample_f() method, which selects an appropriate direction according to the delta distribution. It sets the output variable wi to be the reflection of the supplied direction wo about the surface normal. The *pdf value is set to be one; Section 14.1.3 discusses some subtleties about the mathematical quantity that this value of one represents.

<<BxDF Method Definitions>>+=
Spectrum SpecularReflection::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const { <<Compute perfect specular reflection direction>>
*wi = Vector3f(-wo.x, -wo.y, wo.z);
*pdf = 1; return fresnel->Evaluate(CosTheta(*wi)) * R / AbsCosTheta(*wi); }

The desired incident direction is the reflection of around the surface normal, . This direction can be computed fairly easily using vector geometry. First, note that the incoming direction, the reflection direction, and the surface normal all lie in the same plane. We can decompose vectors that lie in a plane into a sum of two components: one parallel to , which we’ll denote by , and one perpendicular, .

These vectors are easily computed: if and are normalized, then is (Figure 8.7). Because ,

Figure 8.8 shows the setting for computing the reflected direction . We can see that both vectors have the same component, and the value of is the negation of . Therefore, we have

(8.5)

The Reflect() function implements this computation.

<<BSDF Inline Functions>>+=
inline Vector3f Reflect(const Vector3f &wo, const Vector3f &n) { return -wo + 2 * Dot(wo, n) * n; }

In the BRDF coordinate system, , and this expression is substantially simpler.

<<Compute perfect specular reflection direction>>=
*wi = Vector3f(-wo.x, -wo.y, wo.z);

### 8.2.3 Specular Transmission

We will now derive the BTDF for specular transmission. Snell’s law is the basis of the derivation. Not only does Snell’s law give the direction for the transmitted ray, but it can also be used to show that radiance along a ray changes as the ray goes between media with different indices of refraction.

Consider incident radiance arriving at the boundary between two media, with indices of refraction and for the incoming and outgoing media, respectively (Figure 8.9).

We use  to denote the fraction of incident energy that is transmitted to the outgoing direction as given by the Fresnel equations, so . The amount of transmitted differential flux, then, is

If we use the definition of radiance, Equation (5.2), we have

Expanding the solid angles to spherical angles, we have

(8.6)

We can now differentiate Snell’s law with respect to , which gives the relation

Rearranging terms, we have

Substituting this relationship and Snell’s law into Equation (8.6) and then simplifying, we have

Because and therefore , we have the final relationship:

(8.7)

As with the BRDF for specular reflection, we need to divide out a term to get the right BTDF for specular transmission:

where is the specular transmission vector that results from specular transmission of through an interface with surface normal .

The term in this equation corresponds to an easily observed effect: transmission is stronger at near-perpendicular angles. For example, if you look straight down into a clear lake, you can see far into the water, but at grazing angles most of the light is reflected as if from a mirror.

The SpecularTransmission class is almost exactly the same as SpecularReflection except that the sampled direction is the direction for perfect specular transmission. Figure 8.10 shows an image of the dragon model using specular reflection and transmission BRDF and BTDF to model glass.

<<BxDF Declarations>>+=
class SpecularTransmission : public BxDF { public: <<SpecularTransmission Public Methods>>
SpecularTransmission(const Spectrum &T, Float etaA, Float etaB, TransportMode mode) : BxDF(BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)), T(T), etaA(etaA), etaB(etaB), fresnel(etaA, etaB), mode(mode) { } Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); } Spectrum Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const; Float Pdf(const Vector3f &wo, const Vector3f &wi) const { return 0; }
private: <<SpecularTransmission Private Data>>
const Spectrum T; const Float etaA, etaB; const FresnelDielectric fresnel; const TransportMode mode;
};

The SpecularTransmission constructor stores the indices of refraction on both sides of the surface, where etaA is the index of refraction above the surface (where the side the surface normal lies in is “above”), etaB is the index of refraction below the surface, and T gives a transmission scale factor. The TransportMode parameter indicates whether the incident ray that intersected the point where the BxDF was computed started from a light source or whether it was started from the camera. This distinction has implications for how the BxDF’s contribution is computed.

<<SpecularTransmission Public Methods>>=
SpecularTransmission(const Spectrum &T, Float etaA, Float etaB, TransportMode mode) : BxDF(BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)), T(T), etaA(etaA), etaB(etaB), fresnel(etaA, etaB), mode(mode) { }

Because conductors do not transmit light, a FresnelDielectric object is always used for the Fresnel computations.

<<SpecularTransmission Private Data>>=
const Spectrum T; const Float etaA, etaB; const FresnelDielectric fresnel; const TransportMode mode;

As with SpecularReflection, zero is always returned from SpecularTransmission::f(), since the BTDF is a scaled delta distribution.

<<SpecularTransmission Public Methods>>+=
Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); }

Equation (8.7) describes how radiance changes as a ray passes from one medium to another. However, it turns out that while this scaling should be applied for rays starting at light sources, it must not be applied for rays starting from the camera. This issue is discussed in more detail in Section 16.1, and the fragment that applies this scaling, <<Account for non-symmetry with transmission to different medium>>, is defined there.

<<BxDF Method Definitions>>+=
Spectrum SpecularTransmission::Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &sample, Float *pdf, BxDFType *sampledType) const { <<Figure out which is incident and which is transmitted>>
bool entering = CosTheta(wo) > 0; Float etaI = entering ? etaA : etaB; Float etaT = entering ? etaB : etaA;
<<Compute ray direction for specular transmission>>
if (!Refract(wo, Faceforward(Normal3f(0, 0, 1), wo), etaI / etaT, wi)) return 0;
*pdf = 1; Spectrum ft = T * (Spectrum(1.) - fresnel.Evaluate(CosTheta(*wi))); <<Account for non-symmetry with transmission to different medium>>
if (mode == TransportMode::Radiance) ft *= (etaI * etaI) / (etaT * etaT);
return ft / AbsCosTheta(*wi); }

The method first determines whether the incident ray is entering or exiting the refractive medium. pbrt uses the convention that the surface normal, and thus the direction in local reflection space, is oriented such that it points toward the outside of the object. Therefore, if the component of the direction is greater than zero, the incident ray is coming from outside of the object.

<<Figure out which is incident and which is transmitted>>=
bool entering = CosTheta(wo) > 0; Float etaI = entering ? etaA : etaB; Float etaT = entering ? etaB : etaA;

<<Compute ray direction for specular transmission>>=
if (!Refract(wo, Faceforward(Normal3f(0, 0, 1), wo), etaI / etaT, wi)) return 0;

To derive the expression that gives the transmitted direction vector, we can follow a similar approach to the one we used earlier for specular reflection. Figure 8.11 shows the setting.

This time, however, we’ll start with the perpendicular component: if the incident vector is normalized and has perpendicular component , then we know from trigonometry and the definition of that the length of is equal to . Snell’s law tells us that . Negating the direction of and adjusting the length accordingly, we have

Equivalently, because ,

Now for : we know that is parallel to but facing in the opposite direction, and we also know that should be normalized. Putting these together,

The full vector , then, is

Because , the term under the square root is , which gives the final result:

(8.8)

The Refract() function computes the refracted direction wt given an incident direction wi, surface normal n in the same hemisphere as wi, and eta, the ratio of indices of refraction in the incident and transmitted media, respectively. The Boolean return value indicates whether a valid refracted ray was returned in *wt; it is false in the case of total internal reflection.

<<BSDF Inline Functions>>+=
inline bool Refract(const Vector3f &wi, const Normal3f &n, Float eta, Vector3f *wt) { <<Compute using Snell’s law>>
Float cosThetaI = Dot(n, wi); Float sin2ThetaI = std::max(0.f, 1.f - cosThetaI * cosThetaI); Float sin2ThetaT = eta * eta * sin2ThetaI; <<Handle total internal reflection for transmission>>
if (sin2ThetaT >= 1) return false;
Float cosThetaT = std::sqrt(1 - sin2ThetaT);
*wt = eta * -wi + (eta * cosThetaI - cosThetaT) * Vector3f(n); return true; }

Squaring both sides of Snell’s law lets us compute :

<<Compute using Snell’s law>>=
Float cosThetaI = Dot(n, wi); Float sin2ThetaI = std::max(0.f, 1.f - cosThetaI * cosThetaI); Float sin2ThetaT = eta * eta * sin2ThetaI; <<Handle total internal reflection for transmission>>
if (sin2ThetaT >= 1) return false;
Float cosThetaT = std::sqrt(1 - sin2ThetaT);

We need to handle the case of total internal reflection here as well. If the squared value of is greater than or equal to one, total internal reflection has occurred, so false is returned.

<<Handle total internal reflection for transmission>>=
if (sin2ThetaT >= 1) return false;

### 8.2.4 Fresnel-Modulated Specular Reflection and Transmission

For better efficiency in some of the Monte Carlo light transport algorithms to come in Chapters 14, 15, and 16, it’s useful to have a single BxDF that represents both specular reflection and specular transmission together, where the relative weightings of the types of scattering are modulated by the dielectric Fresnel equations. Such a BxDF is provided in FresnelSpecular.

<<BxDF Declarations>>+=
class FresnelSpecular : public BxDF { public: <<FresnelSpecular Public Methods>>
FresnelSpecular(const Spectrum &R, const Spectrum &T, Float etaA, Float etaB, TransportMode mode) : BxDF(BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR)), R(R), T(T), etaA(etaA), etaB(etaB), fresnel(etaA, etaB), mode(mode) { } Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); } Spectrum Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &u, Float *pdf, BxDFType *sampledType) const; Float Pdf(const Vector3f &wo, const Vector3f &wi) const { return 0; }
private: <<FresnelSpecular Private Data>>
const Spectrum R, T; const Float etaA, etaB; const FresnelDielectric fresnel; const TransportMode mode;
};

<<FresnelSpecular Public Methods>>=
FresnelSpecular(const Spectrum &R, const Spectrum &T, Float etaA, Float etaB, TransportMode mode) : BxDF(BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR)), R(R), T(T), etaA(etaA), etaB(etaB), fresnel(etaA, etaB), mode(mode) { }

Since we only focus on the dielectric case, a FresnelDielectric object is always used for the Fresnel computations.

<<FresnelSpecular Private Data>>=
const Spectrum R, T; const Float etaA, etaB; const FresnelDielectric fresnel; const TransportMode mode;

<<FresnelSpecular Public Methods>>+=
Spectrum f(const Vector3f &wo, const Vector3f &wi) const { return Spectrum(0.f); }

Because some of the implementation details depend on principles of Monte Carlo integration that are introduced in Chapter 13, the implementation of the Sample_f() method is in Section 14.1.3.