1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // Interface header.
31 #include "ashikhminbrdf.h"
32 
33 // appleseed.renderer headers.
34 #include "renderer/kernel/lighting/scatteringmode.h"
35 #include "renderer/kernel/shading/directshadingcomponents.h"
36 #include "renderer/modeling/bsdf/bsdf.h"
37 #include "renderer/modeling/bsdf/bsdfsample.h"
38 #include "renderer/modeling/bsdf/bsdfwrapper.h"
39 
40 // appleseed.foundation headers.
41 #include "foundation/math/basis.h"
42 #include "foundation/math/fp.h"
43 #include "foundation/math/fresnel.h"
44 #include "foundation/math/sampling/mappings.h"
45 #include "foundation/math/vector.h"
46 #include "foundation/utility/api/specializedapiarrays.h"
47 #include "foundation/utility/containers/dictionary.h"
48 
49 // Standard headers.
50 #include <cassert>
51 #include <cmath>
52 
53 // Forward declarations.
54 namespace foundation    { class IAbortSwitch; }
55 namespace renderer      { class Assembly; }
56 namespace renderer      { class Project; }
57 
58 using namespace foundation;
59 using namespace std;
60 
61 namespace renderer
62 {
63 
64 namespace
65 {
66     //
67     // Ashikhmin-Shirley BRDF.
68     //
69     // References:
70     //
71     //   http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.4558&rep=rep1&type=pdf
72     //   http://jesper.kalliope.org/blog/library/dbrdfs.pdf
73     //
74 
75     const char* Model = "ashikhmin_brdf";
76 
77     class AshikhminBRDFImpl
78       : public BSDF
79     {
80       public:
AshikhminBRDFImpl(const char * name,const ParamArray & params)81         AshikhminBRDFImpl(
82             const char*                 name,
83             const ParamArray&           params)
84           : BSDF(name, Reflective, ScatteringMode::Diffuse | ScatteringMode::Glossy, params)
85         {
86             m_inputs.declare("diffuse_reflectance", InputFormatSpectralReflectance);
87             m_inputs.declare("diffuse_reflectance_multiplier", InputFormatFloat, "1.0");
88             m_inputs.declare("glossy_reflectance", InputFormatSpectralReflectance);
89             m_inputs.declare("glossy_reflectance_multiplier", InputFormatFloat, "1.0");
90             m_inputs.declare("fresnel_multiplier", InputFormatFloat, "1.0");
91             m_inputs.declare("shininess_u", InputFormatFloat);
92             m_inputs.declare("shininess_v", InputFormatFloat);
93         }
94 
release()95         void release() override
96         {
97             delete this;
98         }
99 
get_model() const100         const char* get_model() const override
101         {
102             return Model;
103         }
104 
sample(SamplingContext & sampling_context,const void * data,const bool adjoint,const bool cosine_mult,const int modes,BSDFSample & sample) const105         void sample(
106             SamplingContext&            sampling_context,
107             const void*                 data,
108             const bool                  adjoint,
109             const bool                  cosine_mult,
110             const int                   modes,
111             BSDFSample&                 sample) const override
112         {
113             const InputValues* values = static_cast<const InputValues*>(data);
114 
115             // Compute reflectance-related values.
116             RVal rval;
117             if (!compute_rval(rval, values))
118                 return;
119 
120             // Compute component weights.
121             float diffuse_weight = ScatteringMode::has_diffuse(modes) ? rval.m_pd : 0.0f;
122             float glossy_weight = ScatteringMode::has_glossy(modes) ? rval.m_pg : 0.0f;
123             const float total_weight = diffuse_weight + glossy_weight;
124             if (total_weight == 0.0f)
125                 return;
126             const float rcp_total_weight = 1.0f / total_weight;
127             diffuse_weight *= rcp_total_weight;
128             glossy_weight *= rcp_total_weight;
129 
130             // Compute shininess-related values.
131             SVal sval;
132             compute_sval(sval, values->m_nu, values->m_nv);
133 
134             // Generate a uniform sample in [0,1)^3.
135             sampling_context.split_in_place(3, 1);
136             const Vector3f s = sampling_context.next2<Vector3f>();
137 
138             ScatteringMode::Mode mode;
139             Vector3f h, incoming;
140             float exp;
141 
142             // Select a component and sample it to compute the incoming direction.
143             if (s[2] < diffuse_weight)
144             {
145                 mode = ScatteringMode::Diffuse;
146 
147                 // Compute the incoming direction in world space.
148                 const Vector3f wi = sample_hemisphere_cosine(Vector2f(s[0], s[1]));
149                 incoming = sample.m_shading_basis.transform_to_parent(wi);
150 
151                 // Compute the halfway vector in world space.
152                 h = normalize(incoming + sample.m_outgoing.get_value());
153 
154                 // Compute the glossy exponent, needed to evaluate the PDF.
155                 const float cos_hn = dot(h, sample.m_shading_basis.get_normal());
156                 const float cos_hu = dot(h, sample.m_shading_basis.get_tangent_u());
157                 const float cos_hv = dot(h, sample.m_shading_basis.get_tangent_v());
158                 const float exp_den = 1.0f - cos_hn * cos_hn;
159                 const float exp_u = values->m_nu * cos_hu * cos_hu;
160                 const float exp_v = values->m_nv * cos_hv * cos_hv;
161                 exp = exp_den == 0.0f ? FP<float>::pos_inf() : (exp_u + exp_v) / exp_den;
162             }
163             else
164             {
165                 mode = ScatteringMode::Glossy;
166 
167                 float cos_phi, sin_phi;
168 
169                 if (sval.m_isotropic)
170                 {
171                     const float phi = s[0] * TwoPi<float>();
172 
173                     cos_phi = cos(phi);
174                     sin_phi = sin(phi);
175 
176                     exp = values->m_nu;
177                 }
178                 else
179                 {
180                     const float phi = sample_anisotropic_glossy(sval.m_k, s[0]);
181 
182                     cos_phi = cos(phi);
183                     sin_phi = sin(phi);
184 
185                     const float exp_u = values->m_nu * cos_phi * cos_phi;
186                     const float exp_v = values->m_nv * sin_phi * sin_phi;
187 
188                     exp = exp_u + exp_v;
189                 }
190 
191                 const float cos_theta = pow(1.0f - s[1], 1.0f / (exp + 1.0f));
192                 const float sin_theta = sqrt(1.0f - cos_theta * cos_theta);
193 
194                 // Compute the halfway vector in world space.
195                 h = sample.m_shading_basis.transform_to_parent(
196                     Vector3f::make_unit_vector(cos_theta, sin_theta, cos_phi, sin_phi));
197 
198                 // Compute the incoming direction in world space.
199                 const Vector3f& outgoing = sample.m_outgoing.get_value();
200                 incoming = reflect(outgoing, h);
201                 if (force_above_surface(incoming, sample.m_geometric_normal))
202                     h = normalize(incoming + outgoing);
203             }
204 
205             // Compute dot products.
206             const Vector3f& shading_normal = sample.m_shading_basis.get_normal();
207             const float cos_in = abs(dot(incoming, shading_normal));
208             const float cos_on = abs(dot(sample.m_outgoing.get_value(), shading_normal));
209             const float cos_oh = min(abs(dot(sample.m_outgoing.get_value(), h)), 1.0f);
210             const float cos_hn = abs(dot(h, shading_normal));
211 
212             float pdf_diffuse = 0.0f, pdf_glossy = 0.0f;
213 
214             if (ScatteringMode::has_diffuse(modes) && diffuse_weight > 0.0f)
215             {
216                 // Evaluate the diffuse component of the BRDF (equation 5).
217                 const float a = 1.0f - pow5(1.0f - 0.5f * cos_in);
218                 const float b = 1.0f - pow5(1.0f - 0.5f * cos_on);
219                 sample.m_value.m_diffuse = rval.m_kd;
220                 sample.m_value.m_diffuse *= a * b;
221                 sample.m_aov_components.m_albedo = values->m_rd;
222 
223                 // Evaluate the PDF of the diffuse component.
224                 pdf_diffuse = cos_in * RcpPi<float>();
225                 assert(pdf_diffuse > 0.0f);
226             }
227 
228             if (ScatteringMode::has_glossy(modes) && glossy_weight > 0.0f)
229             {
230                 // Evaluate the glossy component of the BRDF (equation 4).
231                 const float num = sval.m_kg * pow(cos_hn, exp);
232                 const float den = cos_oh * (cos_in + cos_on - cos_in * cos_on);
233                 fresnel_reflectance_dielectric_schlick(
234                     sample.m_value.m_glossy,
235                     rval.m_scaled_rg,
236                     cos_oh,
237                     values->m_fr_multiplier);
238                 sample.m_value.m_glossy *= num / den;
239 
240                 // Evaluate the PDF of the glossy component (equation 8).
241                 pdf_glossy = num / cos_oh;      // omit division by 4 since num = pdf(h) / 4
242                 assert(pdf_glossy >= 0.0f);
243             }
244 
245             const float probability = diffuse_weight * pdf_diffuse + glossy_weight * pdf_glossy;
246             assert(probability >= 0.0f);
247 
248             if (probability > 1.0e-6f)
249             {
250                 sample.set_to_scattering(mode, probability);
251                 sample.m_incoming = Dual3f(incoming);
252                 sample.m_value.m_beauty = sample.m_value.m_diffuse;
253                 sample.m_value.m_beauty += sample.m_value.m_glossy;
254                 sample.m_min_roughness = 1.0f;
255                 sample.compute_reflected_differentials();
256             }
257         }
258 
evaluate(const void * data,const bool adjoint,const bool cosine_mult,const Vector3f & geometric_normal,const Basis3f & shading_basis,const Vector3f & outgoing,const Vector3f & incoming,const int modes,DirectShadingComponents & value) const259         float evaluate(
260             const void*                 data,
261             const bool                  adjoint,
262             const bool                  cosine_mult,
263             const Vector3f&             geometric_normal,
264             const Basis3f&              shading_basis,
265             const Vector3f&             outgoing,
266             const Vector3f&             incoming,
267             const int                   modes,
268             DirectShadingComponents&    value) const override
269         {
270             const InputValues* values = static_cast<const InputValues*>(data);
271 
272             // Compute reflectance-related values.
273             RVal rval;
274             if (!compute_rval(rval, values))
275                 return 0.0f;
276 
277             // Compute component weights.
278             float diffuse_weight = ScatteringMode::has_diffuse(modes) ? rval.m_pd : 0.0f;
279             float glossy_weight = ScatteringMode::has_glossy(modes) ? rval.m_pg : 0.0f;
280             const float total_weight = diffuse_weight + glossy_weight;
281             if (total_weight == 0.0f)
282                 return 0.0f;
283             const float rcp_total_weight = 1.0f / total_weight;
284             diffuse_weight *= rcp_total_weight;
285             glossy_weight *= rcp_total_weight;
286 
287             // Compute shininess-related values.
288             SVal sval;
289             compute_sval(sval, values->m_nu, values->m_nv);
290 
291             // Compute the halfway vector in world space.
292             const Vector3f h = normalize(incoming + outgoing);
293 
294             // Compute dot products.
295             const Vector3f& shading_normal = shading_basis.get_normal();
296             const float cos_in = abs(dot(incoming, shading_normal));
297             const float cos_on = abs(dot(outgoing, shading_normal));
298             const float cos_oh = abs(dot(outgoing, h));
299             const float cos_hn = abs(dot(h, shading_normal));
300             const float cos_hu = abs(dot(h, shading_basis.get_tangent_u()));
301             const float cos_hv = abs(dot(h, shading_basis.get_tangent_v()));
302 
303             float pdf_diffuse = 0.0f, pdf_glossy = 0.0f;
304 
305             if (ScatteringMode::has_diffuse(modes))
306             {
307                 // Evaluate the diffuse component of the BRDF (equation 5).
308                 const float a = 1.0f - pow5(1.0f - 0.5f * cos_in);
309                 const float b = 1.0f - pow5(1.0f - 0.5f * cos_on);
310                 value.m_diffuse = rval.m_kd;
311                 value.m_diffuse *= a * b;
312 
313                 // Evaluate the PDF of the diffuse component.
314                 pdf_diffuse = cos_in * RcpPi<float>();
315                 assert(pdf_diffuse >= 0.0f);
316             }
317 
318             if (ScatteringMode::has_glossy(modes))
319             {
320                 // Evaluate the glossy component of the BRDF (equation 4).
321                 const float exp_num_u = values->m_nu * cos_hu * cos_hu;
322                 const float exp_num_v = values->m_nv * cos_hv * cos_hv;
323                 const float exp_den = 1.0f - cos_hn * cos_hn;
324                 const float exp = (exp_num_u + exp_num_v) / abs(exp_den);
325                 const float num = cos_hn == 1.0f ? sval.m_kg : sval.m_kg * pow(cos_hn, exp);
326                 const float den = cos_oh * (cos_in + cos_on - cos_in * cos_on);
327                 fresnel_reflectance_dielectric_schlick(
328                     value.m_glossy,
329                     rval.m_scaled_rg,
330                     cos_oh,
331                     values->m_fr_multiplier);
332                 value.m_glossy *= num / den;
333 
334                 // Evaluate the PDF of the glossy component (equation 8).
335                 pdf_glossy = num / cos_oh;      // omit division by 4 since num = pdf(h) / 4
336                 assert(pdf_glossy >= 0.0f);
337             }
338 
339             value.m_beauty = value.m_diffuse;
340             value.m_beauty += value.m_glossy;
341 
342             const float probability = diffuse_weight * pdf_diffuse + glossy_weight * pdf_glossy;
343             assert(probability >= 0.0f);
344 
345             return probability;
346         }
347 
evaluate_pdf(const void * data,const bool adjoint,const Vector3f & geometric_normal,const Basis3f & shading_basis,const Vector3f & outgoing,const Vector3f & incoming,const int modes) const348         float evaluate_pdf(
349             const void*                 data,
350             const bool                  adjoint,
351             const Vector3f&             geometric_normal,
352             const Basis3f&              shading_basis,
353             const Vector3f&             outgoing,
354             const Vector3f&             incoming,
355             const int                   modes) const override
356         {
357             const InputValues* values = static_cast<const InputValues*>(data);
358 
359             // Compute (or retrieve precomputed) reflectance-related values.
360             RVal rval;
361             if (!compute_rval(rval, values))
362                 return 0.0f;
363 
364             // Compute component weights.
365             float diffuse_weight = ScatteringMode::has_diffuse(modes) ? rval.m_pd : 0.0f;
366             float glossy_weight = ScatteringMode::has_glossy(modes) ? rval.m_pg : 0.0f;
367             const float total_weight = diffuse_weight + glossy_weight;
368             if (total_weight == 0.0f)
369                 return 0.0f;
370             const float rcp_total_weight = 1.0f / total_weight;
371             diffuse_weight *= rcp_total_weight;
372             glossy_weight *= rcp_total_weight;
373 
374             // Compute shininess-related values.
375             SVal sval;
376             compute_sval(sval, values->m_nu, values->m_nv);
377 
378             // Compute the halfway vector in world space.
379             const Vector3f h = normalize(incoming + outgoing);
380 
381             float pdf_diffuse = 0.0f, pdf_glossy = 0.0f;
382 
383             if (ScatteringMode::has_diffuse(modes))
384             {
385                 // Evaluate the PDF of the diffuse component.
386                 const float cos_in = abs(dot(incoming, shading_basis.get_normal()));
387                 pdf_diffuse = cos_in * RcpPi<float>();
388                 assert(pdf_diffuse >= 0.0f);
389             }
390 
391             if (ScatteringMode::has_glossy(modes))
392             {
393                 // Evaluate the PDF for the halfway vector (equation 6).
394                 const float cos_oh = abs(dot(outgoing, h));
395                 const float cos_hn = abs(dot(h, shading_basis.get_normal()));
396                 const float cos_hu = dot(h, shading_basis.get_tangent_u());
397                 const float cos_hv = dot(h, shading_basis.get_tangent_v());
398                 const float exp_num_u = values->m_nu * cos_hu * cos_hu;
399                 const float exp_num_v = values->m_nv * cos_hv * cos_hv;
400                 const float exp_den = 1.0f - cos_hn * cos_hn;
401                 const float exp = (exp_num_u + exp_num_v) / abs(exp_den);
402                 const float num = cos_hn == 1.0f ? sval.m_kg : sval.m_kg * pow(cos_hn, exp);
403 
404                 // Evaluate the PDF of the glossy component (equation 8).
405                 pdf_glossy = num / cos_oh;      // omit division by 4 since num = pdf(h) / 4
406                 assert(pdf_glossy >= 0.0f);
407             }
408 
409             const float probability = diffuse_weight * pdf_diffuse + glossy_weight * pdf_glossy;
410             assert(probability >= 0.0f);
411 
412             return probability;
413         }
414 
415       private:
416         typedef AshikhminBRDFInputValues InputValues;
417 
418         // Precomputed reflectance-related values.
419         struct RVal
420         {
421             Spectrum    m_kd;               // constant factor of diffuse component
422             Spectrum    m_scaled_rg;        // glossy reflectance scaled by multiplier
423             float       m_pd;               // probability of diffuse component
424             float       m_pg;               // probability of glossy component
425         };
426 
427         // Precomputed shininess-related values.
428         struct SVal
429         {
430             float       m_kg;               // constant factor of glossy component
431             float       m_k;                // constant factor needed during hemisphere (anisotropic case only)
432             bool        m_isotropic;        // true if the U and V shininess values are the same
433         };
434 
435         template <typename T>
pow5(const T x)436         static T pow5(const T x)
437         {
438             const T x2 = x * x;
439             return x2 * x2 * x;
440         }
441 
compute_rval(RVal & rval,const InputValues * values)442         static bool compute_rval(RVal& rval, const InputValues* values)
443         {
444             // Scale and clamp the diffuse reflectance.
445             Spectrum scaled_rd = values->m_rd;
446             scaled_rd *= values->m_rd_multiplier;
447             saturate_in_place(scaled_rd);
448 
449             // Scale and clamp the glossy reflectance.
450             rval.m_scaled_rg = values->m_rg;
451             rval.m_scaled_rg *= values->m_rg_multiplier;
452             saturate_in_place(rval.m_scaled_rg);
453 
454             // Compute average diffuse and glossy reflectances.
455             const float rd_avg = average_value(scaled_rd);
456             const float rg_avg = average_value(rval.m_scaled_rg);
457             const float sum = rd_avg + rg_avg;
458             if (sum == 0.0f)
459                 return false;
460 
461             // Compute probabilities of glossy and diffuse components.
462             rval.m_pd = rd_avg / sum;
463             rval.m_pg = 1.0f - rval.m_pd;
464             assert(feq(rval.m_pd + rval.m_pg, 1.0f));
465 
466             // Precompute constant factor of diffuse component (equation 5).
467             rval.m_kd.set(1.0f);
468             rval.m_kd -= rval.m_scaled_rg;
469             rval.m_kd *= scaled_rd;
470             rval.m_kd *= 28.0f / (23.0f * Pi<float>());
471             assert(min_value(rval.m_kd) >= 0.0f);
472 
473             return true;
474         }
475 
compute_sval(SVal & sval,const float nu,const float nv)476         static void compute_sval(SVal& sval, const float nu, const float nv)
477         {
478             // Check for isotropicity.
479             sval.m_isotropic = feq(nu, nv, 1.0e-6f);
480 
481             // Precompute constant factor of glossy component (equations 4 and 6).
482             sval.m_kg = sqrt((nu + 1.0f) * (nv + 1.0f)) / (8.0f * Pi<float>());
483 
484             if (!sval.m_isotropic)
485             {
486                 // Precompute constant factor needed during hemisphere sampling.
487                 sval.m_k = sqrt((nu + 1.0f) / (nv + 1.0f));
488             }
489             else
490                 sval.m_k = 0.0f;
491         }
492 
sample_anisotropic_glossy(const float k,const float s)493         static float sample_anisotropic_glossy(const float k, const float s)
494         {
495             if (s < 0.25f)
496             {
497                 // First quadrant.
498                 const float b = tan(HalfPi<float>() * (4.0f * s));
499                 return atan(k * b);
500             }
501             else if (s < 0.5f)
502             {
503                 // Second quadrant.
504                 const float b = tan(HalfPi<float>() * (4.0f * s - 1.0f));
505                 return atan(k * b) + HalfPi<float>();
506             }
507             else if (s < 0.75f)
508             {
509                 // Third quadrant.
510                 const float b = tan(HalfPi<float>() * (4.0f * s - 2.0f));
511                 return atan(k * b) + Pi<float>();
512             }
513             else
514             {
515                 // Fourth quadrant.
516                 const float b = tan(HalfPi<float>() * (4.0f * s - 3.0f));
517                 return atan(k * b) + Pi<float>() + HalfPi<float>();
518             }
519         }
520     };
521 
522     typedef BSDFWrapper<AshikhminBRDFImpl> AshikhminBRDF;
523 }
524 
525 
526 //
527 // AshikhminBRDFFactory class implementation.
528 //
529 
release()530 void AshikhminBRDFFactory::release()
531 {
532     delete this;
533 }
534 
get_model() const535 const char* AshikhminBRDFFactory::get_model() const
536 {
537     return Model;
538 }
539 
get_model_metadata() const540 Dictionary AshikhminBRDFFactory::get_model_metadata() const
541 {
542     return
543         Dictionary()
544             .insert("name", Model)
545             .insert("label", "Ashikhmin-Shirley BRDF");
546 }
547 
get_input_metadata() const548 DictionaryArray AshikhminBRDFFactory::get_input_metadata() const
549 {
550     DictionaryArray metadata;
551 
552     metadata.push_back(
553         Dictionary()
554             .insert("name", "diffuse_reflectance")
555             .insert("label", "Diffuse Reflectance")
556             .insert("type", "colormap")
557             .insert("entity_types",
558                 Dictionary()
559                     .insert("color", "Colors")
560                     .insert("texture_instance", "Texture Instances"))
561             .insert("use", "required")
562             .insert("default", "0.5"));
563 
564     metadata.push_back(
565         Dictionary()
566             .insert("name", "diffuse_reflectance_multiplier")
567             .insert("label", "Diffuse Reflectance Multiplier")
568             .insert("type", "colormap")
569             .insert("entity_types",
570                 Dictionary().insert("texture_instance", "Texture Instances"))
571             .insert("use", "optional")
572             .insert("default", "1.0"));
573 
574     metadata.push_back(
575         Dictionary()
576             .insert("name", "glossy_reflectance")
577             .insert("label", "Glossy Reflectance")
578             .insert("type", "colormap")
579             .insert("entity_types",
580                 Dictionary()
581                     .insert("color", "Colors")
582                     .insert("texture_instance", "Texture Instances"))
583             .insert("use", "required")
584             .insert("default", "0.5"));
585 
586     metadata.push_back(
587         Dictionary()
588             .insert("name", "glossy_reflectance_multiplier")
589             .insert("label", "Glossy Reflectance Multiplier")
590             .insert("type", "colormap")
591             .insert("entity_types",
592                 Dictionary().insert("texture_instance", "Texture Instances"))
593             .insert("use", "optional")
594             .insert("default", "1.0"));
595 
596     metadata.push_back(
597         Dictionary()
598             .insert("name", "fresnel_multiplier")
599             .insert("label", "Fresnel Multiplier")
600             .insert("type", "colormap")
601             .insert("entity_types",
602                 Dictionary().insert("texture_instance", "Texture Instances"))
603             .insert("use", "optional")
604             .insert("default", "1.0"));
605 
606     metadata.push_back(
607         Dictionary()
608             .insert("name", "shininess_u")
609             .insert("label", "Shininess U")
610             .insert("type", "colormap")
611             .insert("entity_types",
612                 Dictionary().insert("texture_instance", "Texture Instances"))
613             .insert("use", "required")
614             .insert("default", "100.0"));
615 
616     metadata.push_back(
617         Dictionary()
618             .insert("name", "shininess_v")
619             .insert("label", "Shininess V")
620             .insert("type", "colormap")
621             .insert("entity_types",
622                 Dictionary().insert("texture_instance", "Texture Instances"))
623             .insert("use", "required")
624             .insert("default", "100.0"));
625 
626     return metadata;
627 }
628 
create(const char * name,const ParamArray & params) const629 auto_release_ptr<BSDF> AshikhminBRDFFactory::create(
630     const char*         name,
631     const ParamArray&   params) const
632 {
633     return auto_release_ptr<BSDF>(new AshikhminBRDF(name, params));
634 }
635 
636 }   // namespace renderer
637