Loading...
Searching...
No Matches
he.h
Go to the documentation of this file.
1#ifndef _BBM_HE_H_
2#define _BBM_HE_H_
3
4#include "bbm/bsdfmodel.h"
5#include "bbm/ndf_sampler.h"
7
8/************************************************************************/
9/*! \file he.h
10
11 \brief Implements: "A Comprehensive Physical Model for Light Reflection" [He
12 1991]: https://doi.org/10.1145/127719.122738
13
14 Follows additonal advice from "Demystifying the He, Torrance, Sillion and
15 Greenberg BRDF The BRDF Model" by Pacanowski and Holzschuch:
16 https://manao.inria.fr/perso/~pac/hugo/posts/hedemystified/ especially the
17 errata:
18 https://manao.inria.fr/perso/~pac/hugo/posts/hedemystified/he_errata.pdf
19
20 Additional information was gleaned from the reference implementation of
21 Westin et al.: http://www.graphics.cornell.edu/~westin/heBRDF/heBRDF.tar.gz
22 with the following bugs fixed in the Vector3 class in Westin et al.'s code:
23 z component in 'length' and 'MakeUnitVector', the z component of 'other' in
24 operator-, operator+, and the y-component in the solution of the cross
25 product.
26
27 Three variants are implemented:
28 + 'he' following the original He et al. paper as close as possible.
29 + 'hewestin' following Westin et al.'s implementation as cose as possible.
30 + 'heholzschuch' following Holzschuch and Pacanowski as close as possible.
31
32 The differences can be summarized by:
33 + differences in Eq. 25; He omits an exponential (as noted in the errata).
34 + differences in Eq 78; Westin uses tau/lambda*2*PI instead of tau.
35 + Westin switches to a explicit rough approximation when g is large.
36 + Holzschuch and Pacanowski fix the number of Taylor terms to 10.
37
38 Experimentally we see that:
39 + The impact of the differences in Shadowing and Masking between with and
40 without and the exponential term is 10-15%.
41 + For sharp materials (i.e., low roughness and low autocorrelation), all three
42 models produce similar results. (within 10%)
43 + For rough materials (either high roughness or high autocorrelation), the
44 different models differ significantly.
45 + 10 Taylor terms (as in Holzschuch and Pacanowski's implementation) is
46 insufficient, and can introduce a significant error.
47
48 Westin et al.'s implementation give the most consistent approximation
49 between the Taylor expansion and the rough approximation, and is therefore
50 likely the most correct.
51
52*************************************************************************/
53
54namespace bbm {
55
56 /**********************************************************************/
57 /*! \brief Masking term scale in Eq 25.
58
59 + WithoutExp = 1.0 (following [He et al. 1991]) + Regular = exp(-
60 (tau*cot(theta) / 2sigma0)^2 ) (following the errata, as well as the
61 reference implementations of Westin et al. and of Holzschuch and
62 Pacanowski)
63 **********************************************************************/
64 enum struct he_eq25
65 {
67 Errata,
68 };
69
70 /**********************************************************************/
71 /*! \brief Eq. 78 variants:
72
73 + Regular: follows He et al. exactly (also in Holzschuch and Pacanowksi).
74 + Westin: uses exp(-g - v_xy2 * (tau/lambda)^2 * Pi^2 / m) instead of
75 exp(-g -v_xy2 * tau^2 / 4m) in Eq. 78.
76 ***********************************************************************/
77 enum struct he_eq78
78 {
79 Regular,
80 Westin,
81 };
82
83
84 /**********************************************************************/
85 /*! \brief The directional specular component of the He et al. BSDF model
86
87 \tparam CONF = bbm configuration
88 \tparam Fresnel = fresnel implementation (requires concepts::fresnel);
89 default = fresnel::complex
90 \tparam EQ25 = determines whether or not to include an extra
91 scaling factor in Eq 25. (Default = he_eq25::WithoutExp)
92 \tparam EQ78 = determines which implementation variant to follow for Eq
93 78. ( Default=he_eq78::Regular)
94 \tparam NewtonRaphsonSteps = number of Newton Raphson steps to take
95 to compute the apparent roughness (Default=4)
96 \tparam TaylorTerms = number of terms in the Taylor expansion for Eq. 78.
97 (Default=40)
98 \tparam AdaptiveTaylor = true if the Talylor approximation should be cut
99 of if the change with additional terms is
100 minimal. If false, all 'TaylorTerms' terms will
101 be computed. (Default = true).
102 \tparam RoughApproxThreshold = 'g' threshold at which the use an
103 approximation instead of the Taylor series.
104 Both solutions are linearly blended within
105 g and g+1. (Default=disabled).
106 \tparam NAME = model name (default = "He")
107
108 Note: the default settings best approximate the original formulation in He
109 et al. 1991
110
111 Note: this only implements the directional specular component
112
113 Implements: concepts::bsdfmodel
114 ***********************************************************************/
115 template<typename CONF,
116 typename Fresnel=fresnel::complex<CONF, Spectrum_t<CONF>>,
119 size_t NewtonRaphsonSteps=4,
120 size_t TaylorTerms=40,
121 bool AdaptiveTaylor=true,
122 literal RoughApproxThreshold=std::numeric_limits<Scalar_t<CONF>>::max(),
123 string_literal NAME="He"> requires concepts::config<CONF> && concepts::fresnel<Fresnel> && concepts::matching_config<CONF, Fresnel>
125 {
126 public:
128 static constexpr string_literal name = NAME;
130
131 /********************************************************************/
132 /*! \brief Evaluate the BSDF for a given in and out direction
133
134 \param in = incident direction
135 \param out = outgoing direction
136 \param component = which reflectance component to eval
137 \param unit = unit of computation (ignored)
138 \param mask = masking of lanes (e.g., for Packet eval)
139 \returns Evaluation of the BSDF per spectrum.
140 *********************************************************************/
141 Spectrum eval(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t /*unit*/=unit_t::Radiance, Mask mask=true) const
142 {
143 // specular?
144 mask &= is_set(component, bsdf_flag::Specular);
145
146 // above surface
147 mask &= (vec::z(in) > 0) && (vec::z(out) > 0);
148
149 // Quick exit
150 if(bbm::none(mask)) return 0;
151
152 // Shadowing
153 Value S_term = S(in, out, mask);
154
155 // Geometrical factor
156 Value G_term = G(in, out, mask);
157
158 // D dsitribution
159 Spectrum D_term = D(in, out, mask);
160
161 // Fresnel
162 Value cosThetaHalf = bbm::safe_sqrt( (1 + bbm::dot(in, out)) / 2.0 );
163 Spectrum F_term = Fresnel::eval(eta, cosThetaHalf, mask);
164
165 // normalization
166 Value normalization = bbm::rcp( Constants::Pi() * spherical::cosTheta(in) * spherical::cosTheta(out) );
167
168 // Done.
169 return bbm::select(mask, normalization * F_term * S_term * G_term * D_term, 0);
170 }
171
172 /********************************************************************/
173 /*! \brief Sample the diffuse BSDF given a direction and two random variables.
174
175 \param out = outgoing direction
176 \param xi = two random variables stored in a Vec2d
177 \param component = which reflectance component to sample
178 \param unit = unit of computation (ignored)
179 \param mask = masking of lanes.
180 \returns A bsdfSample containing the sampled direction and the corresponding pdf.
181 *********************************************************************/
182 BsdfSample sample(const Vec3d& out, const Vec2d& xi, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
183 {
184 // matching flag
185 mask &= is_set(component, bsdf_flag::Specular);
186
187 // Placeholder
188 auto sample = lambertian<Config>().sample(out, xi, bsdf_flag::Diffuse, unit, mask);
190
191 // Done.
192 return sample;
193 }
194
195 /********************************************************************/
196 /*! \brief Compute the pdf given an in and out direction
197
198 \param in = the incoming direction
199 \param out = the outgoing direction
200 \param component = which reflectance component was sampled
201 \param unit = unit of computation
202 \param mask = enable/disable lanes.
203 \returns the PDF that the outgoing direction would be sampled given the incoming direction.
204 *********************************************************************/
205 Value pdf(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
206 {
207 // matching flag
208 mask &= is_set(component, bsdf_flag::Specular);
209
210 // Placeholder
211 return lambertian<Config>().pdf(in, out, bsdf_flag::Diffuse, unit, mask);
212 }
213
214
215 /*****************************************************************/
216 /*! \brief Return the (approximate) hemispherical reflectance of the BSDF
217
218 \param out = the outgoing direction (ignored)
219 \param component = which reflectance component to eval
220 \param unit = unit of computation
221 \param mask = enable/disable lanes
222 \returns the approximate hemispherical reflectance of the BSDF for a given incident direction
223 ******************************************************************/
224 Spectrum reflectance(const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t /*unit*/=unit_t::Radiance, Mask mask=true) const
225 {
226 // specular?
227 mask &= is_set(component, bsdf_flag::Specular);
228
229 // check 'out' lies above the horizon
230 mask &= (vec::z(out) > 0);
231
232 // Quick exit if mask is all negative
233 if(bbm::none(mask)) return 0;
234
235 // Approximate as perfect mirror
236 return Fresnel::eval(eta.value(), vec::z(out), mask) / Constants::Pi() * 4.0;
237 }
238
239 private:
240 /********************************************************************/
241 /*! \brief Shadowing term
242
243 \param in = incident direction of light transport
244 \param out = exitant direction of light transport
245 \param mask = enable/disable lanes
246 \returns fraction of surface visible and illuminated.
247 *********************************************************************/
248 inline constexpr Value S(const Vec3d& in, const Vec3d& out, Mask mask=true) const
249 {
250 return S1(in, mask) * S1(out, mask);
251 }
252
253 /********************************************************************/
254 /*! \brief Mono-directional shadowing term
255
256 Eqs. 24 and 25 from [He et al. 1991]
257
258 \param v = indident/outgoing vector
259 \returns fraction of surface viewed/illuminated
260
261 He et al.'s paper [1991] misses an exponential exp(-(tau*cot/2sigma0)^2)
262 term in Eq. 25 (noted in the errata, and by both Westin et al. and
263 Holzschuch and Pacanowski). Set template EQ25 to
264 he_eq25::Errata to include this factor.
265 *********************************************************************/
266 inline constexpr Value S1(const Vec3d& v, Mask mask=true) const
267 {
268 // Check if smooth
269 Mask smooth = (roughness < Constants::Epsilon());
270
271 // Quick exit
272 if(bbm::all(smooth)) return 1;
273
274 // cot Theta
275 Value cotTheta = bbm::rcp( spherical::tanTheta(v) );
276
277 // erfc(tau*cot/2sigma0)
278 Value scaledCot = autocorrelation * cotTheta / (2.0*roughness);
279 Value erfc = 0.5*bbm::erfc( scaledCot );
280
281 // Lambda (Eq. 25)
282 Value Lambda = 0.5 * Constants::InvSqrtPi() / scaledCot;
283 if constexpr (EQ25 == he_eq25::Errata) Lambda *= bbm::exp(-bbm::pow(scaledCot,2.0));
284 Lambda -= erfc;
285
286 // S (Eq. 24)
287 Value S = bbm::select(smooth, 1.0, (1.0 - erfc) / (Lambda + 1.0));
288
289 // Done,
290 return bbm::select(mask, S, 0.0);
291 }
292
293 /********************************************************************/
294 /*! \brief Compute the Geometrical factor
295
296 Eq. 76 in [He et al. 1991]
297
298 Note: k_i = -in
299 k_r = out
300
301 \param in = incident direction of light transport
302 \param out = outgoing direction of light transport
303 \param mask = enable/disbale lanes
304 \returns the geometrical factor
305 *********************************************************************/
306 inline constexpr Value G(const Vec3d& in, const Vec3d& out, Mask mask=true) const
307 {
308 // k_i = -in
309 // k_r = out
310 // Compute (v.v/z(v))^2, with v = k_r - k_i and n=(0,0,1)
311 Vec3d v = in + out;
312 Value v_scale = bbm::pow(bbm::squared_norm(v) / vec::z(v), 2.0);
313
314 // Omitting i and r subscript, in 'k' and the definition of 's' and 'p':
315 // s = k x n / ||k x n|| (1)
316 // p = s x k (2)
317 //
318 // Note: ||k x n|| = |sin(theta_k)| (3)
319 // Note: k is normalized
320 //
321 // ||k_i x k_r||^2 = sin^2(theta_{i,r}) (using (3))
322 // = 1 - cos^2(theta_{i,r}) (trig)
323 // = 1 - (k_i . k_r)^2 (trig)
324 //
325 // a = {i,r} and b = {r,i}
326 // s_a . k_b = (k_a x n) . k_b / sin(theta_{k_a}) (using (1) an (3))
327 // = n . (k_b x k_a) / sin(theta_{k_a}) (dot of cross)
328 // = (k_bx*k_ay - k_by*k_ax) / sin(theta_{k_a} (n=z => z-component of cross)
329 //
330 // p_a . k_b = (s_a x k_a) . k_b (using (2))
331 // = ((k_a x n) x k_a) . k_b / sin(theta_{k_a}) (using (1) and (3))
332 // = (n(k_a.k_a) - k_a(k_a.n)) . k_b / sin(theta_{k_a}) (cross of cross)
333 // = ((n.k_b)(k_a.k_a) - (k_a.k_b)(k_a.n)) / sin(theta_{k_a}) (bring in dot)
334 // = (k_bz - (k_a.k_b)k_az) / sin(theta_{k_a}) (simplify)
335
336 Value kixn2 = 1 - vec::z(in)*vec::z(in);
337 Value krxn2 = 1 - vec::z(out)*vec::z(out);
338 Value kikr = bbm::dot(-in, out);
339
340 Value sikr = (vec::y(out)*vec::x(in) - vec::x(out)*vec::y(in)); // / kixn
341 Value srki = (vec::y(in)*vec::x(out) - vec::x(in)*vec::y(out)); // / krxn
342 Value pikr = vec::z(out) + kikr*vec::z(in); // / kixn
343 Value prki = vec::z(in) + kikr*vec::z(out); // / krxn * -1
344
345 // Eq. 76: (v.v)/z(v)^2 * ((s_r.k_i)^2 + (p_r.k_i)^2)((s_i.k_r)^2 + (p_i.k_r)^2) / |k_i x k_r|^4
346 Value denom = bbm::pow(1.0 - kikr*kikr, 2.0);
347 Value nom = (bbm::pow(sikr,2.0) + bbm::pow(pikr,2.0)) * (bbm::pow(srki,2.0) + bbm::pow(prki,2.0)) / (krxn2*kixn2);
348 Value g = bbm::select(denom > Constants::Epsilon(), v_scale * nom / denom, 1.0);
349
350 // Done.
351 return bbm::select(mask, g, 0.0);
352 }
353
354 /********************************************************************/
355 /*! \brief Compute the effective surface roughness
356
357 Eq. 80 from [He et al. 1991]. This equation is solved with a
358 Newton-Raphson root finding.
359
360 \param in = incident direction of light transport
361 \param out = outgoing direction of light transport
362 \param mask = enable/disbale lanes
363 \returns the apparent roughness
364 *********************************************************************/
365 inline constexpr Value sigma(const Vec3d& in, const Vec3d& out, Mask mask=true) const
366 {
367 // check if surface is smooth
368 mask &= (roughness > Constants::Epsilon());
369
370 // Quick exit if all masks are false
371 if(bbm::none(mask)) return 0;
372
373 // tan
374 Value tanTheta_i = spherical::tanTheta(in);
375 Value tanTheta_o = spherical::tanTheta(out);
376
377 // Compute K (Eq. 82-83)
378 auto K = [&](const Value& tanTheta) { return tanTheta * bbm::erfc( autocorrelation / (2 * roughness * tanTheta) ); };
379 Value Ki = bbm::select( tanTheta_i > Constants::Epsilon(), K(tanTheta_i), 0 );
380 Value Ko = bbm::select( tanTheta_o > Constants::Epsilon(), K(tanTheta_o), 0 );
381
382 // Set initial solution
383 Value f0 = bbm::rsqrt(Constants::Pi(8.0)) * (Ki + Ko);
384 Value x = bbm::select(f0 <= 1.0, f0, bbm::safe_sqrt(2.0*bbm::log(f0)));
385
386 // Newton-Raphson root finding (emperical observation: tends to converge less than 4 steps)
387 for(size_t step=0; step < NewtonRaphsonSteps; ++step)
388 {
389 // Eval function and derivative
390 Value expn = bbm::exp(0.5*x*x);
391 Value eval = x*expn - f0;
392 Value grad = (1+x*x)*expn;
393
394 // Update root
395 x -= bbm::select(grad > Constants::Epsilon(), eval / grad, 0);
396 }
397
398 // Done.
399 return bbm::select(mask, roughness / bbm::safe_sqrt(1 + x*x), 0.0);
400 }
401
402 /********************************************************************/
403 /*! \brief Distribution Function
404
405 Eq. 78 & 79
406
407 Note Westin et al. uses exp(-g - v_xy2 * (tau/lambda)^2 * Pi^2 / m)
408 instead of exp(-g - v_xy2 * tau^2 / (4m)). This behavior can be set with
409 the EQ78 template parameter (he_eq78::Westin).
410 *********************************************************************/
411 inline constexpr Spectrum D(const Vec3d& in, const Vec3d& out, Mask mask=true) const
412 {
413 // Compute v_xy^2 (Eq. 84)
414 Value v_xy2 = bbm::squared_norm( vec::xy(in) + vec::xy(out) );
415
416 // Compute g (Eq. 79)
417 auto g = bbm::pow( Constants::Pi(2) * sigma(in,out,mask) * (spherical::cosTheta(in) + spherical::cosTheta(out)) / Config::wavelength(), 2.0);
418
419 // Compute exp(-g) normalization term: pi^2 * tau^2 / 4wavelength^2 (Eq. 78)
420 Value tau2 = bbm::pow(autocorrelation, 2.0);
421 auto normalization = Constants::Pi2(0.25) * tau2 / bbm::pow(Config::wavelength(), 2.0);
422
423 // Determine exp_base for (Eq. 78)
424 Spectrum exp_base = v_xy2 * tau2 / 4;
425 if constexpr (EQ78 == he_eq78::Westin) exp_base *= Constants::Pi2(4) / bbm::pow(Config::wavelength(), 2.0);
426
427 // Rough Approximation (Beckmann '63, Section 5.3, Eq. 47, p 87) (excluding normalization)
428 Mask approx = mask && (bbm::hmin(g) > Scalar(RoughApproxThreshold));
429 Spectrum roughApprox = 0;
430 Value weight = 0;
431 if(bbm::any(approx))
432 {
433 roughApprox = bbm::exp( -exp_base / g ) / g;
434
435 // Linearily blend with the Taylor Series when g =
436 // [RoughApproxThreshold, RoughApproxThreshold+1] to avoid
437 // discontinuities.
438 weight = bbm::clamp(bbm::hmin(g) - Scalar(RoughApproxThreshold), 0, 1);
439 }
440
441 // Sum term (Eq. 78) (excluding normalization)
442 Spectrum sum = 0;
443 Spectrum gm = 1;
444 Spectrum last=-1, term=0;
445 Mask converged = !mask || ((bbm::hmin(g)-1) > Scalar(RoughApproxThreshold));
446 for(size_t m=1; m <= TaylorTerms && bbm::any(!converged); ++m)
447 {
448 // copy previous 'term'
449 last = term;
450
451 // compute new term and add to sum.
452 gm *= g / m;
453 term = bbm::exp(-g - exp_base / m) * gm / m;
454 sum += bbm::select(converged, 0, term);
455
456 // Did the Taylor Expansion Converge? We follow Westin et al. and
457 // check that the magnitude of the terms gets small, and are
458 // diminishing (the series tends to increase first and then decrease).
459 if constexpr (AdaptiveTaylor) converged |= (bbm::hmin(term) < Constants::Epsilon()) && (bbm::hmin(term) < bbm::hmin(last));
460 }
461
462 // Blend Taylor Approximation and Rough Approximation
463 auto result = bbm::lerp(sum, roughApprox, weight);
464
465 // Done.
466 return bbm::select(mask, normalization * result, 0);
467 }
468
469 public:
470 ///////////////////////////////
471 //! @{ \name Class Attributes
472 ///////////////////////////////
476
478 //! @}
479
480 //! \brief Default Constructor
482 };
483
485
486 /**********************************************************************/
487 /*! @{ \name He BSDF Variants with data-driven importance sampling
488 **********************************************************************/
489 template<typename CONF, string_literal NAME="He"> requires concepts::config<CONF>
491
492 template<typename CONF, string_literal NAME="HeWestin"> requires concepts::config<CONF>
494
495 template<typename CONF, string_literal NAME="HeHolzschuch"> requires concepts::config<CONF>
497 //! @}
498
499} // end bbm namespace
500
501#endif /* _BBM_HE_H_ */
502
All includes and helpers needed for declaring new bsdfmodels.
#define BBM_EXPORT_BSDFMODEL(BsdfModel)
Definition: bbm_fromstring.h:49
The directional specular component of the He et al. BSDF model.
Definition: he.h:125
Spectrum eval(const Vec3d &in, const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t=unit_t::Radiance, Mask mask=true) const
Evaluate the BSDF for a given in and out direction.
Definition: he.h:141
BsdfSample sample(const Vec3d &out, const Vec2d &xi, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
Sample the diffuse BSDF given a direction and two random variables.
Definition: he.h:182
constexpr Value S1(const Vec3d &v, Mask mask=true) const
Mono-directional shadowing term.
Definition: he.h:266
constexpr Value S(const Vec3d &in, const Vec3d &out, Mask mask=true) const
Shadowing term.
Definition: he.h:248
constexpr Value G(const Vec3d &in, const Vec3d &out, Mask mask=true) const
Compute the Geometrical factor.
Definition: he.h:306
BBM_IMPORT_CONFIG(CONF)
BBM_BSDF_FORWARD
Definition: he.h:129
constexpr Value sigma(const Vec3d &in, const Vec3d &out, Mask mask=true) const
Compute the effective surface roughness.
Definition: he.h:365
Value pdf(const Vec3d &in, const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
Compute the pdf given an in and out direction.
Definition: he.h:205
BBM_ATTRIBUTES(roughness, autocorrelation, eta)
Spectrum reflectance(const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t=unit_t::Radiance, Mask mask=true) const
Return the (approximate) hemispherical reflectance of the BSDF.
Definition: he.h:224
BBM_DEFAULT_CONSTRUCTOR(he_base)
Default Constructor.
Definition: he.h:481
constexpr Spectrum D(const Vec3d &in, const Vec3d &out, Mask mask=true) const
Distribution Function.
Definition: he.h:411
fresnel_parameter< typename std::decay_t< Fresnel >::parameter_type > eta
Definition: he.h:475
bsdf_parameter< Value, bsdf_attr::SpecularParameter, 3.0 > autocorrelation
Definition: he.h:474
static constexpr string_literal name
Definition: he.h:128
bsdf_parameter< Value, bsdf_attr::SpecularParameter, 0.18 > roughness
Definition: he.h:473
Definition: ndf_sampler.h:25
bsdfmodel concept
Definition: bsdfmodel.h:33
config concept
Definition: config.h:31
The classic diffuse Lambertian BSDF model.
#define BBM_CHECK_CONCEPT(CONCEPTNAME, CLASSNAME,...)
Check a class for a concept with bbm::concepts::archetypes in the namespace.
Definition: macro.h:35
T cosTheta(const vec2d< T > &v)
Definition: spherical.h:109
T tanTheta(const vec2d< T > &v)
Definition: spherical.h:177
constexpr decltype(auto) y(bbm::vec3d< T > &v)
Definition: vec.h:23
constexpr decltype(auto) z(bbm::vec3d< T > &v)
Definition: vec.h:26
constexpr decltype(auto) x(bbm::vec3d< T > &v)
Definition: vec.h:20
constexpr const vec2d< T > xy(const vec3d< T > &v)
Definition: vec.h:47
Definition: aggregatebsdf.h:29
constexpr auto select(MASK &&mask, const A &a, const A &b)
Definition: backbone.h:255
he_eq78
Eq. 78 variants:
Definition: he.h:78
he_eq25
Masking term scale in Eq 25.
Definition: he.h:65
constexpr auto is_set(const FLAGNAME &a, const FLAG &flag)
Check if all in 'flag' are also set in 'a'; compatible with packet types.
Definition: flags.h:100
unit_t
Light Unit.
Definition: unit.h:21
Base declaration of attribute; further specialized below.
Definition: attribute.h:26
Replaces a(n isotrpic) BSDF model's importance sampling with a data-driven NDF based sampler.
The classic diffuse Lambertian BSDF model.
Definition: lambertian.h:24
BsdfSample sample(const Vec3d &out, const Vec2d &xi, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
Sample the diffuse BSDF given a direction and two random variables.
Definition: lambertian.h:76
Value pdf(const Vec3d &in, const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t=unit_t::Radiance, Mask mask=true) const
Compute the pdf given an in and out direction.
Definition: lambertian.h:115
Definition: string_literal.h:16