Loading...
Searching...
No Matches
aggregatemodel.h
Go to the documentation of this file.
1#ifndef _BBM_AGGREGATEMODEL_H_
2#define _BBM_AGGREGATEMODEL_H_
3
4#include <numeric>
5
6#include "bbm/bsdfmodel.h"
7#include "util/constforeach.h"
8
9/************************************************************************/
10/*! \file aggregatemodel.h
11 \brief The sum of different BSDF models.
12*************************************************************************/
13
14namespace bbm {
15
16 /**********************************************************************/
17 /*! \brief The sum of different BSDF models.
18
19 \tparam NAME = model name
20 \tparam MODELS = list of BSDF models over which this model aggregates
21 ***********************************************************************/
22 template<string_literal NAME, typename... MODELS> requires (sizeof...(MODELS) >= 2) && concepts::matching_config<MODELS...> && (concepts::bsdfmodel<MODELS> && ...)
23 struct aggregatemodel_base : public MODELS...
24 {
25 BBM_BASETYPES(MODELS...);
26 BBM_IMPORT_CONFIG( std::tuple_element_t<0, std::tuple<MODELS...>> );
27 static constexpr string_literal name = NAME;
29
30 //! \brief Default constructor
31 aggregatemodel_base(void) : MODELS()... {}
32
33 //! \brief Construction from each model
34 aggregatemodel_base(const MODELS&... models) : MODELS(models)... {}
35
36 //! \brief Copy constructor
37 aggregatemodel_base(const aggregatemodel_base& src) : MODELS( static_cast<MODELS>(src) )... {}
38
39 //! \brief Assignment operator
41 {
42 ((static_cast<MODELS&>(*this) = static_cast<MODELS>(src)), ...);
43 return *this;
44 }
45
46
47 /********************************************************************/
48 /*! \brief Evaluate the BSDF for a given in out direction
49
50 \param in = incident direction
51 \param out = outgoing direction
52 \param component = which reflectance component to eval
53 \param unit = unit of computation
54 \param mask = masking of lanes (e.g., for Packet eval)
55 \returns Evaluation of the BSDF per spectrum.
56
57 Evaluation is performed by evaluating each child model, and adding the
58 results.
59 *********************************************************************/
60 Spectrum eval(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
61 {
62 return (MODELS::eval(in, out, component, unit, mask) + ...);
63 }
64
65 /********************************************************************/
66 /*! \brief Sample the aggregate BSDF given a direction and two random variables.
67
68 \param out = outgoing direction
69 \param xi = two random variables stored in a Vec2d
70 \param component = which reflectance component to sample
71 \param unit = unit of computation
72 \param mask = masking of lanes.
73 \returns A bsdfSample containing the sampled direction and the corresponding pdf.
74
75 Selects one BSDF model to sample based with a probability propertional
76 to the hemispherical reflectances of each BSDF. The selected BSDF model
77 is then sampled, and its sampled direction is returned. The PDF is
78 computed as the weighted sum of each BSDF model's PDF and the
79 reflectance weights.
80 *********************************************************************/
81 BsdfSample sample(const Vec3d& out, const Vec2d& xi, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
82 {
83 BsdfSample result = {0,0,bsdf_flag::None};
84
85 // Gather weight per PDF (weight ~ reflectance)
86 std::array<Value, sizeof...(MODELS)> weights = {bbm::hsum(MODELS::reflectance(out, component, unit, mask)) ...};
87
88 // Normalization factor
89 auto sum = std::accumulate(std::begin(weights), std::end(weights), Value(0));
90
91 // Iterate through all MODELS and select the model to sample based on xi[0]:
92 // 1) there are few models, so there is not much to gain from a binary search
93 // 2) when using packet types, we likely need to evaluate all models.
94 auto weight = std::begin(weights);
95 auto xi0 = xi[0] * sum;
96
97 CONSTFOREACH(MODEL, MODELS,
98 {
99 // check if xi0 falls in [0...weight]
100 Mask m = mask && (xi0 >= 0 && xi0 <= *weight);
101
102 // Normalize xi0 to [0...1]
103 Value normalized_xi0 = bbm::select( m && (*weight > Constants::Epsilon()), xi0 / *weight, 0);
104
105 // If in range, sample; otherwise keep current solution
106 // NOTE: Using static_cast<MODEL*>(this)-> instead of MODEL::otherwise
107 // 'this' might not be captured in CONSTFOREACH lambda.
108 result = bbm::select(m, static_cast<const MODEL*>(this)->sample(out, Vec2d(normalized_xi0, xi[1]), component, unit, m), result);
109
110 // prepare for next MODEL
111 xi0 -= *weight;
112 weight++;
113 });
114
115 // Compute PDF
116 std::array<Value, sizeof...(MODELS)> pdfs = {MODELS::pdf(result.direction, out, component, unit, mask) ...};
117 result.pdf = bbm::select(sum > Constants::Epsilon(), std::inner_product(std::begin(pdfs), std::end(pdfs), std::begin(weights), Value(0)) / sum, 0);
118
119 // Done.
120 return result;
121 }
122
123
124 /********************************************************************/
125 /*! \brief Compute the pdf given an in and out direction
126
127 \param in = the incoming (sampled) direction
128 \param out = the outgoing (given) direction
129 \param component = which reflectance component was sampled
130 \param unit = unit of computation
131 \param mask = masking of lanes
132 \returns the PDF that the incoming direction would be sampled given the outgoing direction.
133
134 The PDF is computed as the reflectance weighted sum of all PDFs.
135 *********************************************************************/
136 Value pdf(const Vec3d& in, const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
137 {
138 // Check masks
139 if(bbm::none(mask)) return 0;
140
141 // Gather all PDFs
142 std::array<Value, sizeof...(MODELS)> pdfs = {MODELS::pdf(in, out, component, unit, mask) ...};
143
144 // Gather weight per PDF (weight ~ reflectance)
145 std::array<Value, sizeof...(MODELS)> weights = {bbm::hsum(MODELS::reflectance(out, component, unit, mask)) ...};
146
147 // Return normalized weighted sum
148 auto sum = std::accumulate(std::begin(weights), std::end(weights), Value(0));
149 return bbm::select(sum > Constants::Epsilon(), std::inner_product(std::begin(pdfs), std::end(pdfs), std::begin(weights), Value(0)) / sum, 0);
150 }
151
152
153 /********************************************************************/
154 /*! \brief Return the (approximate) hemispherical reflectance of the BSDF
155
156 \param out = the outgoing direction
157 \param component = which reflectance component to eval
158 \param unit = unit of computation
159 \param mask = masking of lanes
160 \returns the approximate reflectance of the BSDF for a given direction
161
162 The hemispherical reflectance is computed as the sum of the underlying
163 BSDF models.
164 *********************************************************************/
165 Spectrum reflectance(const Vec3d& out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
166 {
167 // Check mask
168 if(bbm::none(mask)) return Spectrum(0);
169
170 // Sum reflectances
171 return (MODELS::reflectance(out, component, unit, mask) + ...);
172 }
173
174 /********************************************************************/
175 /*! \brief toString to overwrite default printing behavior
176 ********************************************************************/
177 inline std::string toString(void) const
178 {
179 std::string str;
180 CONSTFOREACH(MODEL, MODELS,
181 {
182 if(!str.empty()) str += ", ";
183 str += bbm::toString( static_cast<const MODEL&>(*this) );
184 });
185
186 return std::string(name) + "(" + str + ")";
187 }
188
189 /********************************************************************/
190 /*! \brief construct an aggregate model from a string
191 ********************************************************************/
192 static inline aggregatemodel_base fromString(const std::string& str)
193 {
194 auto [key, value] = bbm::string::get_keyword(str);
195
196 // check name
197 if(key != std::string(name)) throw std::invalid_argument(std::string("BBM: mismatched object name ") + key + ", expected: " + std::string(name));
198
199 // split
201
202 // check if number of models corresponds
203 if(sizeof...(MODELS) != args.size()) throw std::invalid_argument(std::string("BBM: expected ") + std::to_string(sizeof...(MODELS)) + " models, only found " + std::to_string(args.size()) + " in: " + str);
204
205 // create models
206 auto make_aggregate = [&]<size_t... IDX>(std::index_sequence<IDX...>)
207 {
208 return aggregatemodel_base( bbm::fromString<MODELS>(args[IDX])... );
209 };
210
211 // Done.
212 return make_aggregate(std::make_index_sequence<sizeof...(MODELS)>{});
213 }
214 };
215
216 /**********************************************************************/
217 /*! \brief The sum of different BSDF models.
218
219 \tparam MODELS = list of BSDF models over which this model aggregates
220 ***********************************************************************/
221 template<typename... MODELS>
222 using aggregatemodel = aggregatemodel_base<"Aggregate"_sl, MODELS...>;
223
224 /**********************************************************************/
225 /*! \brief Method for simplifying the creation of an aggregate model
226
227 \param models = comma separated list of models.
228
229 Based on the list of BSDF models, an aggregate model is created and
230 initialized with the parameters of the models in the parameter list.
231 ***********************************************************************/
232 template<typename... MODELS> requires (concepts::bsdfmodel<MODELS> && ...)
233 aggregatemodel<MODELS...> aggregate(const MODELS&... models) { return aggregatemodel<MODELS...>(models...); }
234
235} // end bbm namespace
236
237#endif /* _BBM_AGGREGATEMODEL_H_ */
All includes and helpers needed for declaring new bsdfmodels.
bsdfmodel concept
Definition: bsdfmodel.h:33
Compile-time for each loop.
#define CONSTFOREACH(ITR_TYPE, TYPE_LIST,...)
HELPER MACRO.
Definition: constforeach.h:53
std::pair< std::string, std::string > get_keyword(const std::string &str)
Return the keyword substring appearing an open bracket, and the arguments appearing in the brackets: ...
Definition: string_util.h:65
std::string remove_brackets(const std::string &str)
remove surrounding backets
Definition: string_util.h:37
std::vector< std::string > split_args(const std::string &str)
Split a string based on comma's if not surrounded by brackets.
Definition: string_util.h:94
Definition: aggregatebsdf.h:29
constexpr auto select(MASK &&mask, const A &a, const A &b)
Definition: backbone.h:255
std::string toString(const T &)
toString alias
Definition: stringconvert.h:594
auto aggregate(const bsdf< BSDFMODELs > &... src)
Helper methods for simplifying the creation of a aggregate bsdf.
Definition: aggregatebsdf.h:329
decltype(auto) value(T &&t)
return the value of an attribute, or if not an attribute the object
Definition: attribute_value.h:20
unit_t
Light Unit.
Definition: unit.h:21
The sum of different BSDF models.
Definition: aggregatemodel.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 aggregate BSDF given a direction and two random variables.
Definition: aggregatemodel.h:81
aggregatemodel_base & operator=(const aggregatemodel_base &src)
Assignment operator.
Definition: aggregatemodel.h:40
aggregatemodel_base(void)
Default constructor.
Definition: aggregatemodel.h:31
std::string toString(void) const
toString to overwrite default printing behavior
Definition: aggregatemodel.h:177
static aggregatemodel_base fromString(const std::string &str)
construct an aggregate model from a string
Definition: aggregatemodel.h:192
BBM_BSDF_FORWARD
Definition: aggregatemodel.h:28
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: aggregatemodel.h:136
aggregatemodel_base(const aggregatemodel_base &src)
Copy constructor.
Definition: aggregatemodel.h:37
Spectrum eval(const Vec3d &in, const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
Evaluate the BSDF for a given in out direction.
Definition: aggregatemodel.h:60
Spectrum reflectance(const Vec3d &out, BsdfFlag component=bsdf_flag::All, unit_t unit=unit_t::Radiance, Mask mask=true) const
Return the (approximate) hemispherical reflectance of the BSDF.
Definition: aggregatemodel.h:165
BBM_IMPORT_CONFIG(std::tuple_element_t< 0, std::tuple< MODELS... > >)
static constexpr string_literal name
Definition: aggregatemodel.h:27
aggregatemodel_base(const MODELS &... models)
Construction from each model.
Definition: aggregatemodel.h:34
Forward declaration.
Definition: args.h:248
static constexpr size_t size
Definition: args.h:282
Definition: string_literal.h:16