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