Loading...
Searching...
No Matches
lowsmooth.h
Go to the documentation of this file.
1#ifndef _BBM_LOW_SMOOTH_H_
2#define _BBM_LOW_SMOOTH_H_
3
4#include "bbm/bsdfmodel.h"
5
6/************************************************************************/
7/*! \file lowsmooth.h
8
9 \brief The Low et al.'s smooth BRDF model from "BRDF models for accurate and
10 efficient rendering of glossy surfaces" [Low 2012]:
11 https://doi.org/10.1145/2077341.2077350
12
13*************************************************************************/
14
15namespace bbm {
16
17 template<typename CONF, string_literal NAME="LowSmooth"> requires concepts::config<CONF>
19 {
20 public:
22 static constexpr string_literal name = NAME;
24
25 /********************************************************************/
26 /*! \brief Evaluate the BSDF for a given in and out direction
27
28 \param in = incident direction
29 \param out = outgoing direction
30 \param component = which reflectance component to eval
31 \param unit = unit of computation (ignored)
32 \param mask = masking of lanes (e.g., for Packet eval)
33 \returns Evaluation of the BSDF per spectrum.
34 *********************************************************************/
35 Spectrum eval(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t /*unit*/=unit_t::Radiance, Mask mask=true) const
36 {
37 // specular?
38 mask &= is_set(component, bsdf_flag::Specular);
39
40 // above surface?
41 mask &= (vec::z(in) >= 0) && (vec::z(out) >= 0);
42
43 // Quick exit if mask is all negative
44 if(bbm::none(mask)) return Spectrum(0);
45
46 // Projected In and Out vectors
47 Vec2d Ip = vec::xy(in);
48 Vec2d Op = vec::xy(out);
49
50 // Dp^2 (eq. 10) & cosThetaD (eq. 14)
51 Value Dp2 = bbm::squared_norm(Ip + Op);
52 Value cosThetaD = bbm::safe_sqrt(1 - 0.25*bbm::squared_norm(Ip-Op));
53
54 // Eval (G == 1)
55 Value S = bbm::pow(1.0 + B*Dp2, -C);
56 Value Q = fresnel::cook<Config>::eval(eta.value(), cosThetaD, mask);
57
58 // Done (eq. 12).
59 return bbm::select(mask, A*S*Q, 0);
60 }
61
62 /********************************************************************/
63 /*! \brief Sample the BSDF given a direction and two random variables.
64
65 \param out = outgoing direction
66 \param xi = two random variables stored in a Vec2d
67 \param component = which reflectance component to sample
68 \param unit = unit of computation (ignored)
69 \param mask = masking of lanes.
70 \returns A bsdfSample containing the sampled direction and the corresponding pdf.
71 *********************************************************************/
72 BsdfSample sample(const Vec3d& out, const Vec2d& xi, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
73 {
74 BsdfSample sample = {0,0,bsdf_flag::None};
75
76 // specular?
77 mask &= is_set(component, bsdf_flag::Specular);
78
79 // random variable in [0...1]?
80 mask &= (xi[0] >= 0) && (xi[1] >= 0) && (xi[0] <= 1) && (xi[1] <= 1);
81
82 // Quick exit if mask is all negative
83 if(bbm::none(mask)) return sample;
84
85 // ro2
86 Value ro2 = spherical::sinTheta2(out);
87
88 // normalization factor*Pi (eq 19):
89 Value temp = 1.0 + (2*B*(1.0 + ro2)) + bbm::pow(B*(1-ro2), 2);
90 temp = -bbm::log(2.0) + bbm::log(1 + B*(1-ro2) + bbm::safe_sqrt(temp));
91 Value MdPi = B * bbm::rcp(temp); // InvPi cancels out with Pi in E.
92
93 // E (eq. 22)
94 Value E = 2.0 * bbm::exp( xi[0] * B * bbm::rcp(MdPi) );
95
96 // Sample in disc (eq. 20)
97 Value ri = bbm::safe_sqrt( (E-2)*(E + 2*B*ro2) / (2*E*B) );
98
99 // Sample in disc (eq. 21)
100 Value ro = bbm::sqrt(ro2);
101 Value scale = bbm::sqrt( (1.0 + B*bbm::pow(ri + ro, 2.0)) / (1.0 + B*bbm::pow(ri - ro, 2.0)) );
102 Value phi_i = 2.0 * bbm::atan( bbm::tan(xi[1] * Constants::Pi()) * scale ) + spherical::phi(out);
103
104 // Fill in
105 sample.direction = bbm::select(mask, vec::expand( bbm::cossin(phi_i) * ri, bbm::safe_sqrt(1.0 - ri*ri) ), 0);
106 sample.pdf = pdf(sample.direction, out, component, unit, mask);
107 sample.flag = bbm::select(mask, BsdfFlag(bsdf_flag::Specular), BsdfFlag(bsdf_flag::None));
108
109 // Done.
110 return sample;
111 }
112
113 /********************************************************************/
114 /*! \brief Compute the pdf given an in and out direction
115
116 \param in = the incoming (sampled) direction
117 \param out = the outgoing (given) direction
118 \param component = which reflectance component was sampled
119 \param unit = unit of computation (ignored)
120 \param mask = masking of lanes
121 \returns the PDF that the incoming direction would be sampled given the outgoing direction.
122 ***********************************************************************/
123 Value pdf(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t /*unit*/=unit_t::Radiance, Mask mask=true) const
124 {
125 // specular?
126 mask &= is_set(component, bsdf_flag::Specular);
127
128 // above surface?
129 mask &= (vec::z(in) >= 0) && (vec::z(out) >= 0);
130
131 // Quick bailout
132 if(bbm::none(mask)) return 0;
133
134 // ro2 = sin(theta_o)^2
135 Value ro2 = spherical::sinTheta2(out);
136
137 // normalization factor (eq 19):
138 Value temp = 1.0 + (2*B*(1.0 + ro2)) + bbm::pow(B*(1.0-ro2), 2);
139 temp = -bbm::log(Value(2)) + bbm::log(1 + B*(1-ro2) + bbm::safe_sqrt(temp));
140 Value Md = B * Constants::InvPi() * bbm::rcp(temp);
141
142 // PDF (eq 18)
143 Value pdf = Md / (1.0 + B*bbm::squared_norm(vec::xy(in) + vec::xy(out)));
144
145 // Done (eq. 17)
146 return bbm::select(mask, pdf * spherical::cosTheta(in), 0);
147 }
148
149 /*******************************************************************/
150 /*! \brief Return the (approximate) hemispherical reflectance of the BSDF
151
152 \param out = the outgoing direction
153 \param component = which reflectance component to eval
154 \param unit = unit of computation (ignored)
155 \param mask = masking of lanes
156 \returns the approximate hemsispherical reflectance of the BSDF for a given direction
157 ********************************************************************/
158 Spectrum reflectance(const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t /*unit*/=unit_t::Radiance, Mask mask=true) const
159 {
160 // specular?
161 mask &= is_set(component, bsdf_flag::Specular);
162
163 // check 'out' lies above the horizon
164 mask &= (vec::z(out) > 0);
165
166 // Quick exit if mask is all negative
167 if(bbm::none(mask)) return 0;
168
169 // Ignore 'out' (assume = [0,0,1]), then S = A / (1+B*sin(Theta)^2)^C
170 // C==1 => log(B+1) / 2B
171 // C!=1 => 1.0 - (B+1)^(1-C) / (2B(C-1))
172 Value factor = bbm::select( bbm::abs(C-1) < Constants::Epsilon(), bbm::log(B + 1) / (2*B), (1.0 - bbm::pow(B+1, 1-C)) / (2*B*(C-1)));
173
174 // Fresnel == Reflectance at normal incidence.
175 Value R0 = bbm::pow((eta - 1) / (eta + 1), 2);
176
177 // Done.
178 return bbm::select(mask, Constants::Pi(2)*A*factor*R0, 0);
179 }
180
181 ////////////////////////////////
182 //! @{ \name Class Attributes
183 ////////////////////////////////
188
190 //! @}
191
192 //! \brief Default constructor
194 };
195
197
198} // end bbm namespace
199
200#endif /* _BBM_LOW_SMOOTH_H_ */
201
All includes and helpers needed for declaring new bsdfmodels.
#define BBM_EXPORT_BSDFMODEL(BsdfModel)
Definition: bbm_fromstring.h:49
Definition: lowsmooth.h:19
bsdf_parameter< Value, bsdf_attr::SpecularParameter > B
Definition: lowsmooth.h:185
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: lowsmooth.h:35
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 BSDF given a direction and two random variables.
Definition: lowsmooth.h:72
BBM_IMPORT_CONFIG(CONF)
bsdf_parameter< Spectrum, bsdf_attr::SpecularParameter > A
Definition: lowsmooth.h:184
fresnel_parameter< ior::ior< Value > > eta
Definition: lowsmooth.h:187
BBM_BSDF_FORWARD
Definition: lowsmooth.h:23
BBM_DEFAULT_CONSTRUCTOR(lowsmooth)
Default constructor.
Definition: lowsmooth.h:193
bsdf_parameter< Value, bsdf_attr::SpecularParameter > C
Definition: lowsmooth.h:186
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: lowsmooth.h:158
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: lowsmooth.h:123
static constexpr string_literal name
Definition: lowsmooth.h:22
BBM_ATTRIBUTES(A, B, C, eta)
bsdfmodel concept
Definition: bsdfmodel.h:33
#define BBM_CHECK_CONCEPT(CONCEPTNAME, CLASSNAME,...)
Check a class for a concept with bbm::concepts::archetypes in the namespace.
Definition: macro.h:35
T & phi(vec2d< T > &v)
Definition: spherical.h:39
T sinTheta2(const vec2d< T > &v)
Definition: spherical.h:77
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
constexpr auto select(MASK &&mask, const A &a, const A &b)
Definition: backbone.h:255
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
static constexpr std::decay_t< parameter_type >::type eval(const parameter_type &param, const Value &cosTheta, Mask mask=true)
Evaluate Fresnel reflectance.
Definition: fresnel_cook.h:41
Definition: string_literal.h:16