Loading...
Searching...
No Matches
studentt.h
Go to the documentation of this file.
1#ifndef _BBM_STUDENT_T_NDF_H_
2#define _BBM_STUDENT_T_NDF_H_
3
4#include "bbm/ndf.h"
5
6/************************************************************************/
7/*! \file std.h
8
9 \brief Implements the STD (Student's T-Distribution) Microfacet Normal
10 Distribution from: "STD: Student’s t-Distribution of Slopes for Microfacet
11 Based BSDFs" [Ribardiere et al. 2017] https://doi.org/10.1111/cgf.13137
12
13*************************************************************************/
14
15namespace bbm {
16 namespace ndf {
17
18 /********************************************************************/
19 /*! /brief The StudentT Microfacet distribution
20
21 \tparam CONF = bbm configuration
22 \tparam Symmetry = isotropic or anisotropic (Default = symmetry_v::Isoptric)
23 \tparam NAME = ndf name (default = STDist)
24
25 Implements: concepts::ndf
26 ********************************************************************/
27 template<typename CONF, symmetry_v Symmetry=symmetry_v::Isotropic, string_literal NAME="StudentT"> requires concepts::config<CONF>
29 {
30 public:
32 static constexpr string_literal name = NAME;
33
34 /********************************************************************/
35 /*! \brief Evaluate the NDF
36
37 \param halfway = vector to eval the NDF for
38 \param mask = enable/disbale lanes [default = true]
39 \returns the resulting evaluation of the NDF
40 *********************************************************************/
41 Value eval(const Vec3d& halfway, Mask mask=true) const
42 {
43 // above surface?
44 mask &= (vec::z(halfway) > 0);
45
46 // Quick exit
47 if(bbm::none(mask)) return 0;
48
49 // eval NDF
50 Vec2d alpha(roughness);
51 Value alpha2 = bbm::hprod(alpha);
52 Value normalization = Constants::Pi() * alpha2 * bbm::pow(spherical::cosTheta(halfway), 4);
53 Value denumerator = bbm::pow(1.0 + bbm::squared_norm(vec::xy(halfway)/alpha) / ((gamma-1)*bbm::pow(vec::z(halfway), 2)), gamma);
54
55 // Done.
56 return bbm::select(mask, bbm::rcp(normalization * denumerator), 0);
57 }
58
59 /********************************************************************/
60 /*! \brief Sample the NDF
61
62 \param view = view direction (ignored)
63 \param xi = 2D uniform random variables in [0..1] range
64 \param Mask = enable/disbale lanes
65 \returns A sampled microfacet normal.
66 ********************************************************************/
67 Vec3d sample(const Vec3d& /*view*/, const Vec2d& xi, Mask mask=true) const
68 {
69 // check valid xi
70 mask &= (xi[0] >= 0) && (xi[1] >= 0) && (xi[0] <= 1) && (xi[1] <= 1);
71
72 // quick exit
73 if(bbm::none(mask)) return 0;
74
75 // Get sin(phi) and cos(phi) & normalization
76 Vec2d csp = bbm::cossin( Constants::Pi(2) * xi[0] );
77 Value normalization;
78
79 if constexpr (is_set(Symmetry, symmetry_v::Anisotropic)) // strech if anisotropic
80 {
81 normalization = bbm::rcp(bbm::squared_norm(csp / roughness));
82 csp = bbm::normalize(csp * roughness);
83 }
84 else normalization = roughness*roughness;
85
86 // compute sin(theta) and cos(theta)
87 Value tanTheta2 = (bbm::pow(xi[1], 1.0/(1.0-gamma)) - 1) * (gamma-1) * normalization;
88 Value cosTheta = bbm::rsqrt(1.0 + tanTheta2);
89 Value sinTheta = bbm::safe_sqrt(1.0 - cosTheta*cosTheta);
90
91 // Done.
92 return bbm::select(mask, vec::expand(csp*sinTheta, cosTheta), 0);
93 }
94
95 /********************************************************************/
96 /*! \brief PDF of sampling the NDF
97
98 \param view = view direction (ignored)
99 \param m = sampled microfacet normal
100 \param mask = enable/disable lanes [default = true]
101 \returns the PDF of sampling 'm' using the sample method.
102 *********************************************************************/
103 Value pdf(const Vec3d& /*view*/, const Vec3d& m, Mask mask=true) const
104 {
105 // m above surface?
106 mask &= (vec::z(m) > 0);
107
108 // quick bail out
109 if(bbm::none(mask)) return 0;
110
111 // eval PDF: D(m) |m.n|
112 Value pdf = eval(m, mask) * spherical::cosTheta(m);
113
114 // ignore negative values
115 mask &= (pdf > 0);
116
117 // Done.
118 return select(mask, pdf, 0);
119 }
120
121 /********************************************************************/
122 /*! \brief Monodirectional shadowing and masking term
123
124 \param v = incident/outgoing vector
125 \param m = microfacet normal
126 \\param mask = enable/disable lanes
127 \returns the monodirectional shadowing and masking attentuation factor
128
129 There does not exist a closed form solution; use the same polynomial
130 approximation as for Beckmann, except with a different 'a' value.
131 *******************************************************************/
132 Value G1(const Vec3d& v, const Vec3d& m, Mask mask=true) const
133 {
134 // check (dot(v,m) / dot(v,n)) > 0.
135 mask &= (vec::z(v) > 0) && (bbm::dot(v, m) > 0);
136
137 // Quick exit
138 if(bbm::none(mask)) return 0;
139
140 // check for normal incidence
141 auto normalMask = (vec::z(v) < 1.0-Constants::Epsilon());
142 if(bbm::none(normalMask)) return 1.0;
143
144 // compute z = mu/sigma (Unnumbered Eq. between Eq. 18 and Eq. 19)
145 Vec2d alpha(roughness);
146 Value z = vec::z(v) * bbm::rsqrt( bbm::squared_norm(vec::xy(v) * alpha) );
147
148 // S1: (Eq. 14)
149 Value S1 = bbm::pow( (gamma-1) + z*z, 3.0/2.0 - gamma ) / z;
150
151 // S2: (Eq. 22 & 23) (approx of Eq. 15)
152 Value S2 = F21(z) * (F22(gamma) + F23(gamma)*F24(z));
153
154 // Lambda: (Eq. 13)
155 Value S1_scale = bbm::pow(gamma-1, gamma) / (2*gamma - 3);
156 Value lambda = bbm::select(normalMask, bbm::tgamma(gamma - 0.5) / bbm::tgamma(gamma) * Constants::InvSqrtPi() * (S1_scale*S1 + bbm::sqrt(gamma-1)*S2) - 0.5, 0.0);
157
158 // G1: (Eq 7)
159 return bbm::select(mask, 1.0 / (1.0 + lambda), 0);
160 }
161
162 private:
163 //! \brief F21(z) (Eq. 23)
164 static inline Value F21(const Value& z)
165 {
166 Value z2 = z*z; Value z3 = z2*z;
167 Value numerator = 1.066*z + 2.655*z2 + 4.892*z3;
168 Value denumerator = 1.038 + 2.969*z + 4.305*z2 + 4.418*z3;
169 return (numerator / denumerator);
170 }
171
172 //! \brief F22(gamma) (Eq. 23)
173 static inline Value F22(const Value& gamma)
174 {
175 Value gamma2 = gamma*gamma; Value gamma3 = gamma2*gamma;
176 Value numerator = 14.402 - 27.145*gamma + 20.574*gamma2 - 2.745*gamma3;
177 Value denumerator = -30.612 + 86.567*gamma - 84.341*gamma2 + 29.938*gamma3;
178 return (numerator / denumerator);
179 }
180
181 //! \brief F23(gamma) (Eq. 23)
182 static inline Value F23(const Value& gamma)
183 {
184 Value gamma2 = gamma*gamma; Value gamma3 = gamma2*gamma;
185 Value numerator = -129.404 + 324.987*gamma - 299.305*gamma2 + 93.268*gamma3;
186 Value denumerator = -92.609 + 256.006*gamma - 245.663*gamma2 + 86.064*gamma3;
187 return (numerator / denumerator);
188 }
189
190 //! \brief F24(z) (Eq. 23)
191 static inline Value F24(const Value& z)
192 {
193 Value z2 = z*z; Value z3 = z2*z;
194 Value numerator = 6.537 + 6.074*z - 0.623*z2 + 5.223*z3;
195 Value denumerator = 6.538 + 6.103*z - 3.218*z2 + 6.347*z3;
196 return (numerator / denumerator);
197 }
198 public:
199
200 ///////////////////////////////
201 //! @{ \name Class Attributes
202 ///////////////////////////////
204 bsdf_parameter<Value, bsdf_attr::SpecularParameter, 2.0, 40.0, 1.5 + Constants::Epsilon()> gamma;
205
207 //! @}
208
209 //! \brief Default constructor
211 };
212
214
215 } // end ndf namespace
216} // end bbm namespace
217
218#endif /* _BBM_STUDENT_T_NDF_H_ */
All includes and helpers needed for declaring new ndfs.
Definition: studentt.h:29
Value G1(const Vec3d &v, const Vec3d &m, Mask mask=true) const
Monodirectional shadowing and masking term.
Definition: studentt.h:132
static Value F24(const Value &z)
F24(z) (Eq. 23)
Definition: studentt.h:191
bsdf_parameter< Value, bsdf_attr::SpecularParameter, 2.0, 40.0, 1.5+Constants::Epsilon()> gamma
Definition: studentt.h:204
BBM_ATTRIBUTES(roughness, gamma)
Value pdf(const Vec3d &, const Vec3d &m, Mask mask=true) const
PDF of sampling the NDF.
Definition: studentt.h:103
Value eval(const Vec3d &halfway, Mask mask=true) const
Evaluate the NDF.
Definition: studentt.h:41
Vec3d sample(const Vec3d &, const Vec2d &xi, Mask mask=true) const
Sample the NDF.
Definition: studentt.h:67
BBM_DEFAULT_CONSTRUCTOR(studentt)
Default constructor.
Definition: studentt.h:210
static Value F23(const Value &gamma)
F23(gamma) (Eq. 23)
Definition: studentt.h:182
specular_roughness< symmetry_t< Symmetry, Value > > roughness
Definition: studentt.h:203
static Value F21(const Value &z)
F21(z) (Eq. 23)
Definition: studentt.h:164
static constexpr string_literal name
Definition: studentt.h:32
static Value F22(const Value &gamma)
F22(gamma) (Eq. 23)
Definition: studentt.h:173
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 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 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
auto tgamma(const TA &a, const TX &x)
Unnormalized incomplete upper gamma function.
Definition: gamma.h:532
Base declaration of attribute; further specialized below.
Definition: attribute.h:26
Definition: string_literal.h:16