Loading...
Searching...
No Matches
ggx.h
Go to the documentation of this file.
1#ifndef _BBM_GGX_NDF_H_
2#define _BBM_GGX_NDF_H_
3
4#include "bbm/ndf.h"
5
6/************************************************************************/
7/*! \file ggx.h
8
9 \brief Implements the GGX Microfacet Normal Distribution from: "Microfacet
10 Models for Refraction through Rough Surfaces" [Walter et al. 2007]:
11 http://dx.doi.org/10.2312/EGWR/EGSR07/195-206
12
13 Anisotropic GGX distribution follows: "Understanding the Masking-Shadowing
14 Function in Microfacet-Based BRDFs" [Heitz 2014]:
15 https://jcgt.org/published/0003/02/03/
16
17 Following the importance sampling of visible normals from: "A Simpler and
18 Exact Sampling Routine for the GGX Distribution of Visible Normals" [Heitz
19 2017]: https://jcgt.org/published/0007/04/01/
20
21***********************************************************************/
22
23namespace bbm {
24 namespace ndf {
25
26 /**********************************************************************/
27 /*! \brief The GGX Microfacet distribution
28
29 \tparam CONF = bbm configuration
30 \tpatam Symmetry = symmetry_v::Isotropic or symmetry_v::Anisotropic (Default = symmetry_v::Isotropic)
31 \tparam SampleVisible = sample visible normal distribution (Default = true)
32 \tparam NAME = ndf name (default = GGX)
33
34 Implements: concepts::ndf
35 ************************************************************************/
36 template<typename CONF, symmetry_v Symmetry=symmetry_v::Isotropic, bool SampleVisible=true, string_literal NAME="GGX"> requires concepts::config<CONF>
37 class ggx
38 {
39 public:
41 static constexpr string_literal name = NAME;
42
43 /********************************************************************/
44 /*! \brief Evaluate the NDF
45
46 \param halfway = vector to eval the NDF for
47 \param mask = enable/disbale lanes [default = true]
48 \returns the resulting evaluation of the NDF
49 *********************************************************************/
50 Value eval(const Vec3d& halfway, Mask mask=true) const
51 {
52 // above surface?
53 mask &= (vec::z(halfway) > 0);
54
55 // Quick exit
56 if(bbm::none(mask)) return 0;
57
58 // eval NDF
59 Vec2d alpha(roughness);
60 Value alpha2 = bbm::hprod(alpha);
61 Value D = bbm::rcp( Constants::Pi() * alpha2 * bbm::pow( bbm::squared_norm(vec::xy(halfway) / alpha) + bbm::pow(vec::z(halfway), 2), 2.0 ) );
62
63 // Done.
64 return bbm::select(mask, D, 0);
65 }
66
67 /********************************************************************/
68 /*! \brief Sample the NDF
69
70 \param view = view direction
71 \param xi = 2D uniform random variables in [0..1] range
72 \param Mask = enable/disbale lanes
73 \returns A sampled microfacet normal.
74 ********************************************************************/
75 Vec3d sample(const Vec3d& view, const Vec2d& xi, Mask mask=true) const
76 {
77 // check valid xi
78 mask &= (xi[0] >= 0) && (xi[1] >= 0) && (xi[0] <= 1) && (xi[1] <= 1);
79
80 // quick exit
81 if(bbm::none(mask)) return 0;
82
83 // sample microfacet normal
84 if constexpr (SampleVisible) // Following [Heitz 2017]
85 {
86 Vec2d alpha(roughness);
87
88 // 1) Stretch
89 Vec3d view_s = bbm::normalize( vec::expand(vec::xy(view)*alpha, vec::z(view)) );
90
91 // 2) Orthonormal basis
92 Vec3d T1 = bbm::select(vec::z(view_s) < 1.0-Constants::Epsilon(), bbm::normalize(bbm::cross(view_s, Vec3d(0,0,1))), Vec3d(1,0,0));
93 Vec3d T2 = bbm::cross(T1, view_s);
94
95 // 3) sample polar coordinates
96 Value a = bbm::rcp(1.0 + vec::z(view_s));
97 Value r = bbm::sqrt(xi[0]);
98 Value phi = bbm::select(xi[1] < a, xi[1]/a, 1.0 + (xi[1]-a) / (1.0-a)) * Constants::Pi();
99 Vec2d csp = bbm::cossin(phi);
100 Value P1 = r*csp[0];
101 Value P2 = bbm::select(xi[1] < a, 1.0, vec::z(view_s)) * r * csp[1];
102
103 // 4) normal
104 Vec3d normal = P1*T1 + P2*T2 + bbm::safe_sqrt(1.0 - P1*P1 - P2*P2)*view_s;
105
106 // 5) unstretch
107 return bbm::normalize( vec::expand(vec::xy(normal) * alpha, bbm::max(0.0, vec::z(normal))) );
108 }
109
110 else // if constexpr (!SampleVisible) // Following [Walter 2007] (w/ anisoptric extension)
111 {
112 // 1) get sin(phi) an cos(phi) & normalization
113 Vec2d csp = bbm::cossin( Constants::Pi(2) * xi[0] );
114 Value normalization;
115
116 if constexpr (is_set(Symmetry, symmetry_v::Anisotropic)) // stretch if anisotropic
117 {
118 csp *= roughness;
119 normalization = bbm::squared_norm(csp);
120 csp *= bbm::rsqrt(normalization); // normalize sin & cos
121 }
122 else normalization = roughness*roughness; // isotropic normalization
123
124 // 2) compute sin(theta) and cos(theta)
125 Value tanTheta2 = normalization * (xi[1] / (1-xi[1]));
126 Value cosTheta = bbm::rsqrt(1.0 + tanTheta2);
127 Value sinTheta = bbm::safe_sqrt(1.0 - cosTheta*cosTheta);
128
129 // Done.
130 return bbm::select(mask, vec::expand(csp*sinTheta, cosTheta), 0);
131 }
132 }
133
134 /********************************************************************/
135 /*! \brief PDF of sampling the NDF
136
137 \param view = view direction (ignored)
138 \param m = sampled microfacet normal
139 \param mask = enable/disable lanes [default = true]
140 \returns the PDF of sampling 'm' using the sample method.
141 *********************************************************************/
142 Value pdf(const Vec3d& view, const Vec3d& m, Mask mask=true) const
143 {
144 // m above surface?
145 mask &= (vec::z(m) > 0);
146
147 // quick bail out
148 if(bbm::none(mask)) return 0;
149
150 // eval PDF:
151 Value pdf = eval(m, mask);
152
153 if constexpr (SampleVisible)
154 pdf *= G1(view, m, mask) * bbm::abs(bbm::dot(view, m)) / spherical::cosTheta(view);
155 else // if constexpr (!SampleVisible)
157
158 // ignore negative values (round off errors)
159 mask &= (pdf > 0);
160
161 // Done.
162 return bbm::select(mask, pdf, 0);
163 }
164
165 /********************************************************************/
166 /*! \brief Monodirectional shadowing and masking term
167
168 \param v = incident/outgoing vector
169 \param m = microfacet normal
170 \param mask = enable/disable lanes
171 \returns the monodirectional shadowing and masking attentuation factor
172 *******************************************************************/
173 Value G1(const Vec3d& v, const Vec3d& m, Mask mask=true) const
174 {
175 // check (dot(v,m) / dot(v,n)) > 0.
176 mask &= (vec::z(v) > 0) && (bbm::dot(v, m) > 0);
177
178 // Quick exit
179 if(bbm::none(mask)) return 0;
180
181 // eval
182 Vec2d r(roughness);
183 Value roughness2 = bbm::hprod(r);
184 Value tanTheta2 = spherical::tanTheta2(v);
185 Value denom = 1.0 + bbm::sqrt(1.0 + roughness2*tanTheta2);
186
187 // Done.
188 return bbm::select(mask, 2.0 / denom, 0.0);
189 }
190
191 ///////////////////////////////
192 //! @{ \name Class Attributes
193 ///////////////////////////////
195
197 //! @}
198
199 //! \brief Default constructor
201 };
202
204
205 } // end ndf namespace
206} // end bbm namespace
207
208#endif /* _BBM_GGX_NDF_H_ */
All includes and helpers needed for declaring new ndfs.
The GGX Microfacet distribution.
Definition: ggx.h:38
Value G1(const Vec3d &v, const Vec3d &m, Mask mask=true) const
Monodirectional shadowing and masking term.
Definition: ggx.h:173
BBM_IMPORT_CONFIG(CONF)
Value pdf(const Vec3d &view, const Vec3d &m, Mask mask=true) const
PDF of sampling the NDF.
Definition: ggx.h:142
Value eval(const Vec3d &halfway, Mask mask=true) const
Evaluate the NDF.
Definition: ggx.h:50
Vec3d sample(const Vec3d &view, const Vec2d &xi, Mask mask=true) const
Sample the NDF.
Definition: ggx.h:75
BBM_ATTRIBUTES(roughness)
specular_roughness< symmetry_t< Symmetry, Value > > roughness
Definition: ggx.h:194
BBM_DEFAULT_CONSTRUCTOR(ggx)
Default constructor.
Definition: ggx.h:200
static constexpr string_literal name
Definition: ggx.h:41
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
T tanTheta2(const vec2d< T > &v)
Definition: spherical.h:183
T cosTheta(const vec2d< T > &v)
Definition: spherical.h:109
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 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
vec3d< T > halfway(const vec3d< T > &a, const vec3d< T > &b)
Halfway vector (3D)
Definition: vec_transform.h:77
constexpr vec3d< T > cross(const vec3d< T > &a, const vec3d< T > &b)
Cross product of two 3D vectors.
Definition: vec_transform.h:62
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
Definition: string_literal.h:16