Loading...
Searching...
No Matches
beckmann.h
Go to the documentation of this file.
1#ifndef _BBM_BECKMANN_NDF_H_
2#define _BBM_BECKMANN_NDF_H_
3
4#include "bbm/ndf.h"
5
6/************************************************************************/
7/*! \file beckmann.h
8
9 \brief Implements the Beckman Microfacet Normal Distribution:
10
11 Beckmann, P. AND Spizzichino, A. "The Scattering of Electromagnetic Waves
12 from Rough Surfaces". MacMillan, New York, 1963, pp. 1-33, 70-98.
13
14 Following the description from: "Microfacet Models for Refraction through
15 Rough Surfaces" [Walter et al. 2007]: http://dx.doi.org/10.2312/EGWR/EGSR07/195-206
16
17 Following the importance sampling of visible norals from: "An Improved
18 Visible Normal Sampling Routine for the Beckmann Distribution" [Jakob 2014]
19
20***********************************************************************/
21
22namespace bbm {
23 namespace ndf {
24
25 /**********************************************************************/
26 /*! \brief The Beckman Microfacet distribution
27
28 \tparam CONF = bbm configuration
29 \tparam Symmetry = symmetry_v::Isotropic or symmetry_v::Anisotropic (Default = symmetry_v::Isotropic)
30 \tparam Normalize = whether or not to normalize the NDF by 1/pi (Default = true)
31 \tparam SampleVisible = sample visible normal distribution (Default = true)
32 \tparam NAME = ndf name (default = Beckmann)
33
34 Implements: concepts::ndf
35 ************************************************************************/
36 template<typename CONF, symmetry_v Symmetry=symmetry_v::Isotropic, bool Normalize=true, bool SampleVisible=true, string_literal NAME="Beckmann"> requires concepts::config<CONF>
37 struct beckmann
38 {
40 static constexpr string_literal name = NAME;
41
42 /********************************************************************/
43 /*! \brief Evaluate the NDF
44
45 \param halfway = vector to eval the NDF for
46 \param mask = enable/disbale lanes [default = true]
47 \returns the resulting evaluation of the NDF
48 *********************************************************************/
49 Value eval(const Vec3d& halfway, Mask mask=true) const
50 {
51 // above surface?
52 mask &= (vec::z(halfway) > 0);
53
54 // Quick exit
55 if(bbm::none(mask)) return 0;
56
57 // eval NDF
58 Vec2d alpha(roughness);
59 Value cosTheta2 = spherical::cosTheta2(halfway);
60 Value D = bbm::exp( -bbm::squared_norm(vec::xy(halfway) / alpha) / cosTheta2 ) / (vec::u(alpha) * vec::v(alpha) * cosTheta2 * cosTheta2);
61
62 if constexpr (Normalize) D *= Constants::InvPi();
63
64 // Done.
65 return bbm::select(mask, D, 0);
66 }
67
68 /********************************************************************/
69 /*! \brief Sample the NDF
70
71 \param view = view direction
72 \param xi = 2D uniform random variables in [0..1] range
73 \param Mask = enable/disbale lanes
74 \returns A sampled microfacet normal.
75 ********************************************************************/
76 Vec3d sample(const Vec3d& view, const Vec2d& xi, Mask mask=true) const
77 {
78 // check valid xi
79 mask &= (xi[0] >= 0) && (xi[1] >= 0) && (xi[0] <= 1) && (xi[1] <= 1);
80
81 // quick exit
82 if(bbm::none(mask)) return 0.0;
83
84 // sample microfacet normal
85 if constexpr (SampleVisible) // Following [Jakob 2014]
86 {
87 Vec2d alpha(roughness);
88
89 // 1) Stretch
90 Vec3d view_s = bbm::normalize( vec::expand(vec::xy(view)*alpha, vec::z(view)) );
91
92 // 2) sample P22
93 Value tanTheta = spherical::tanTheta(view_s);
94
95 Value maxval = bbm::erf(bbm::rcp(tanTheta));
96 Vec2d xic = bbm::clamp(xi, 10e-6, 1.0-10e-6);
97 Value x = maxval - (maxval + 1) * bbm::erf(bbm::sqrt(-bbm::log(xic[0])));
98
99 xic[0] *= 1.0 + maxval + Constants::InvSqrtPi() * tanTheta * bbm::exp(-spherical::cosTheta2(view_s));
100
101 for(size_t i=0; i < 3; ++i) // 3 Newton iterations
102 {
103 Value slope = bbm::erfinv(x);
104 Value val = 1.0 + x + Constants::InvSqrtPi() * tanTheta * bbm::exp(-slope*slope) - xic[0];
105 Value derivative = 1.0 - slope * tanTheta;
106 x -= val / derivative;
107 }
108
109 Vec2d slope = bbm::select(x > -1.0 && x < +1.0, bbm::erfinv(Vec2d(x, 2.0*xic[1] - 1.0)), 0);
110
111 // 3) rotate and unstretch
112 Vec2d slope_u = rotation2d(spherical::cossinPhi(view_s)) * slope * alpha;
113
114 // Done.
115 return bbm::select(mask, bbm::normalize( vec::expand(-slope_u, 1) ), 0.0);
116 }
117
118 else // if constexpr (!SampleVisible) // Following [Walter 2007] (w/ anisotropic extension)
119 {
120 // 1) get sin(phi) and cos(phi) & normalization
121 Vec2d csp = bbm::cossin( Constants::Pi(2) * xi[0] );
122 Value normalization;
123
124 if constexpr (is_set(Symmetry, symmetry_v::Anisotropic)) // stretch if anisotropic
125 {
126 csp *= roughness;
127 normalization = bbm::squared_norm(csp);
128 csp *= bbm::rsqrt(normalization);
129 }
130 else normalization = roughness*roughness; // isotropic normalization
131
132 // 2) compute sin(theta) and cos(theta)
133 Value cosTheta = bbm::rsqrt(1.0 - normalization*bbm::log(xi[1]));
134 Value sinTheta = bbm::safe_sqrt(1.0 - cosTheta*cosTheta);
135
136 // Done.
137 return bbm::select(mask, vec::expand(csp*sinTheta, cosTheta), 0.0);
138 }
139 }
140
141 /********************************************************************/
142 /*! \brief PDF of sampling the NDF
143
144 \param view = view direction
145 \param m = sampled microfacet normal
146 \param mask = enable/disable lanes [default = true]
147 \returns the PDF of sampling 'm' using the sample method.
148 *********************************************************************/
149 Value pdf(const Vec3d& view, const Vec3d& m, Mask mask=true) const
150 {
151 // m above surface?
152 mask &= (vec::z(m) > 0);
153
154 // quick bail out
155 if(bbm::none(mask)) return 0;
156
157 // eval PDF
158 Value pdf = eval(m, mask);
159
160 if constexpr (SampleVisible)
161 pdf *= G1(view, m, mask) * bbm::abs(bbm::dot(view, m)) / spherical::cosTheta(view);
162 else // if constexpr (!SampleVisible)
164
165 // ignore negative values (round off errors)
166 mask &= (pdf > 0);
167
168 // Done.
169 return bbm::select(mask, pdf, 0);
170 }
171
172 /********************************************************************/
173 /*! \brief Monodirectional shadowing and masking term
174
175 \param v = incident/outgoing vector
176 \param m = microfacet normal
177 \\param mask = enable/disable lanes
178 \returns the monodirectional shadowing and masking attentuation factor
179 *******************************************************************/
180 Value G1(const Vec3d& v, const Vec3d& m, Mask mask=true) const
181 {
182 // check (dot(v,m) / dot(v,n)) > 0.
183 mask &= (vec::z(v) > 0) && (bbm::dot(v, m) > 0);
184
185 // Quick exit
186 if(bbm::none(mask)) return 0;
187
188 // compute 'a'
189 Value a;
190 if constexpr (is_set(Symmetry, symmetry_v::Anisotropic))
191 {
192 a = bbm::rsqrt( bbm::squared_norm(vec::xy(v)*roughness) / bbm::pow(vec::z(v), 2) );
193 }
194 else a = bbm::rcp(roughness * spherical::tanTheta(v));
195
196 // eval
197 Value g = bbm::select (a < 1.6, (3.535*a + 2.181*a*a) / (1 + 2.276*a + 2.577*a*a), 1.0);
198
199 // rational approximation
200 return bbm::select(mask, g, 0.0);
201 }
202
203 ///////////////////////////////
204 //! @{ \name Class Attributes
205 ///////////////////////////////
207
209 //! @}
210
211 //! \brief Default constructor
213 };
214
216
217 } // end ndf namespace
218} // end bbm namespace
219
220#endif /* _BBM_BECKMANN_NDF_H_ */
All includes and helpers needed for declaring new ndfs.
config concept
Definition: config.h:31
ndf concept
Definition: ndf.h:29
#define BBM_CHECK_CONCEPT(CONCEPTNAME, CLASSNAME,...)
Check a class for a concept with bbm::concepts::archetypes in the namespace.
Definition: macro.h:35
vec2d< T > cossinPhi(const vec2d< T > &v)
Definition: spherical.h:153
T cosTheta(const vec2d< T > &v)
Definition: spherical.h:109
T tanTheta(const vec2d< T > &v)
Definition: spherical.h:177
bbm::value_t< V > cosTheta2(const V &v)
Definition: spherical.h:115
constexpr decltype(auto) u(bbm::vec2d< T > &v)
Definition: vec.h:37
constexpr const vec3d< T > expand(const vec2d< T > &v, V &&a)
Definition: vec.h:55
constexpr decltype(auto) z(bbm::vec3d< T > &v)
Definition: vec.h:26
constexpr decltype(auto) v(bbm::vec2d< T > &v)
Definition: vec.h:40
constexpr const vec2d< T > xy(const vec3d< T > &v)
Definition: vec.h:47
Definition: aggregatebsdf.h:29
symmetry_v
symmetry variants.
Definition: bsdf_symmetry.h:19
constexpr auto select(MASK &&mask, const A &a, const A &b)
Definition: backbone.h:255
mat2d< std::decay_t< T > > rotation2d(const vec2d< T > &cossin)
2D rotation matrix
Definition: transform.h:101
vec3d< T > halfway(const vec3d< T > &a, const vec3d< T > &b)
Halfway vector (3D)
Definition: vec_transform.h:77
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
Base declaration of attribute; further specialized below.
Definition: attribute.h:26
The Beckman Microfacet distribution.
Definition: beckmann.h:38
Value G1(const Vec3d &v, const Vec3d &m, Mask mask=true) const
Monodirectional shadowing and masking term.
Definition: beckmann.h:180
Value pdf(const Vec3d &view, const Vec3d &m, Mask mask=true) const
PDF of sampling the NDF.
Definition: beckmann.h:149
Value eval(const Vec3d &halfway, Mask mask=true) const
Evaluate the NDF.
Definition: beckmann.h:49
BBM_DEFAULT_CONSTRUCTOR(beckmann)
Default constructor.
Definition: beckmann.h:212
Vec3d sample(const Vec3d &view, const Vec2d &xi, Mask mask=true) const
Sample the NDF.
Definition: beckmann.h:76
BBM_ATTRIBUTES(roughness)
specular_roughness< symmetry_t< Symmetry, Value > > roughness
Definition: beckmann.h:206
static constexpr string_literal name
Definition: beckmann.h:40
Definition: string_literal.h:16