Fermat
path_inversion.h
1 /*
2  * Fermat
3  *
4  * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the NVIDIA CORPORATION nor the
14  * names of its contributors may be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #pragma once
30 
31 // ------------------------------------------------------------------------- //
32 //
33 // Declaration of classes used to represent and manipulate paths.
34 //
35 // ------------------------------------------------------------------------- //
36 
37 #include <path.h>
38 #include <bsdf.h>
39 #include <camera.h>
40 #include <lights.h>
41 #include <renderer.h>
42 #include <mesh_utils.h>
43 
46 
49 
50 #define MAXIMUM_INVERSION_ERROR 1.0e-6f
51 
52 #define TRANSFORM_PDF(_p, _w, _w_norm) (_p *= _w_norm / _w)
53 //#define TRANSFORM_PDF(_p, _w, _w_norm) (_p *= _w / _w_norm)
54 //#define TRANSFORM_PDF(_p, _w, _w_norm) (_p *= _w)
55 //#define TRANSFORM_PDF(_p, _w, _w_norm) (_p /= _w)
56 
60 {
61  enum ComponentSelectionStrategy
62  {
63  kWeightedComponentSelection = 0,
64  kUniformComponentSelection = 1,
65  kPdfComponentSelection = 3,
66  kWeightedPdfComponentSelection = 4,
67  kBrdfComponentSelection = 5,
68  };
69 
70  enum PdfType
71  {
72  kDirectTransformPdf = 0,
73  kInverseTransformPdf = 1,
74  };
75 
76  enum Order
77  {
78  DR = Bsdf::kDiffuseReflectionIndex,
79  DT = Bsdf::kDiffuseTransmissionIndex,
80  GR = Bsdf::kGlossyReflectionIndex,
81  GT = Bsdf::kGlossyTransmissionIndex,
82  };
83 
89  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
90  void setup(
91  ComponentSelectionStrategy _strategy,
92  const Bsdf& bsdf,
93  const VertexGeometry& geom,
94  const cugar::Vector3f& in,
95  const cugar::Vector3f& out,
96  const bool _RR = false);
97 
100  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
102 
105  template <typename TRandomGenerator>
106  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
107  bool invert(
108  const Bsdf& bsdf,
109  const VertexGeometry& geom,
110  const cugar::Vector3f& in,
111  const cugar::Vector3f& out,
112  TRandomGenerator& random,
113  cugar::Vector3f& z,
114  BsdfInverse::PdfType pdf_type = BsdfInverse::kDirectTransformPdf,
115  float* p = NULL,
116  float* p_proj = NULL);
117 
120  template <typename TRandomGenerator>
121  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
122  bool invert_component(
123  const Bsdf& bsdf,
124  const VertexGeometry& geom,
125  const cugar::Vector3f& in,
126  const cugar::Vector3f& out,
127  const Bsdf::ComponentType out_comp,
128  TRandomGenerator& random,
129  cugar::Vector3f& z,
130  const bool output_global_coordinates = true);
131 
138  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
139  float BsdfInverse::pdf(
140  const Bsdf::ComponentType out_comp,
141  cugar::SphericalMeasure measure = cugar::kProjectedSolidAngle,
142  const bool weighted = false) const;
143 
146  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
147  float BsdfInverse::pdf_comp(
148  const Bsdf::ComponentType out_comp) const;
149 
152  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
153  float BsdfInverse::weight(
154  const Bsdf::ComponentType out_comp) const;
155 
156 public:
157  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
158  bool precompute_component_selection_coefficients();
159 
160  ComponentSelectionStrategy strategy;
161  float w[4];
162  float p_comp[4];
163  float p_comp_proj[4];
164  float p_sel[4];
165  cugar::Vector3f f_comp[4];
166  bool RR;
167 };
168 
169 template <typename TRandomGenerator, typename BsdfComponent>
170 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
171 bool invert_layer(
172  const BsdfComponent& bsdf_comp,
173  const VertexGeometry& v_prev,
174  const cugar::Vector3f& in,
175  const cugar::Vector3f& out,
176  TRandomGenerator& random,
177  cugar::Vector3f& z,
178  float& p,
179  float& p_proj)
180 {
181  // invert the diffuse component
182  if (bsdf_comp.invert(
183  v_prev,
184  in,
185  out,
186  random,
187  z,
188  p,
189  p_proj) == false)
190  return false;
191 
192  // make sure the inversion process was precise enough
193  if (1)
194  {
195  cugar::Vector3f out_;
196  cugar::Vector3f g_;
197  float p_, p_proj_;
198 
199  bsdf_comp.sample(
200  z,
201  v_prev,
202  in,
203  out_,
204  g_,
205  p_,
206  p_proj_);
207 
208  //const float err = cugar::max3(
209  // fabsf(out_.x - out.x),
210  // fabsf(out_.y - out.y),
211  // fabsf(out_.z - out.z));
212  const float err = 1.0f - dot(out_, out);
213  if (err > MAXIMUM_INVERSION_ERROR)
214  {
215  /*printf("err %f\n N: %f %f %f\n I: %f %f %f\n, O: %f %f %f\n O: %f %f %f\n",
216  err,
217  v_prev.normal_s.x, v_prev.normal_s.y, v_prev.normal_s.z,
218  in.x, in.y, in.z,
219  out.x, out.y, out.z,
220  out_.x, out_.y, out_.z);*/
221  return false;
222  }
223 
224  // make sure the output throughput is finite
225  if (!cugar::is_finite(g_.x) ||
226  !cugar::is_finite(g_.y) ||
227  !cugar::is_finite(g_.z))
228  return false;
229  }
230  return true;
231 }
232 
233 template <typename TRandomGenerator>
234 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
235 bool invert_bsdf(
236  const Bsdf& bsdf,
237  const VertexGeometry& v_prev,
238  const cugar::Vector3f& in,
239  const cugar::Vector3f& out,
240  TRandomGenerator& random,
241  cugar::Vector3f& z,
242  float& p,
243  float& p_proj,
244  bool RR = true)
245 {
246  // choose the BSDF component utilized for inversion
247  float diffuse_refl_prob;
248  float diffuse_trans_prob;
249  float glossy_refl_prob;
250  float glossy_trans_prob;
251 
252  bsdf.sampling_weights(v_prev, in, diffuse_refl_prob, diffuse_trans_prob, glossy_refl_prob, glossy_trans_prob);
253 
254  float w1 = diffuse_refl_prob;
255  float w2 = glossy_refl_prob;
256  float w3 = diffuse_trans_prob;
257  float w4 = glossy_trans_prob;
258  const float w_sum = w1 + w2 + w3 + w4;
259 
260  const float w1_norm = w1 / w_sum;
261  const float w2_norm = w2 / w_sum;
262  const float w3_norm = w3 / w_sum;
263  const float w4_norm = w4 / w_sum;
264 
265  if (RR == false)
266  {
267  w1 = w1_norm;
268  w2 = w2_norm;
269  w3 = w3_norm;
270  w4 = w4_norm;
271  }
272 
273  const float v = random.next();
274  if (v < w1_norm)
275  {
276  // invert the diffuse component
277  if (invert_layer(
278  bsdf.diffuse(),
279  v_prev,
280  in,
281  out,
282  random,
283  z,
284  p,
285  p_proj) == false)
286  return false;
287 
288  // transform the z component into the desired range [0,w1)
289  z.z = z.z * w1;
290  }
291  else if (v < w1_norm + w2_norm)
292  {
293  // invert the glossy component
294  if (invert_layer(
295  bsdf.glossy(),
296  v_prev,
297  in,
298  out,
299  random,
300  z,
301  p,
302  p_proj) == false)
303  return false;
304 
305  // transform the z component into the desired range [w1,w2)
306  z.z = z.z * w2 + w1;
307  }
308  else if (v < w1_norm + w2_norm + w3_norm)
309  {
310  // invert the diffuse transmission component
311  if (invert_layer(
312  bsdf.diffuse_trans(),
313  v_prev,
314  in,
315  out,
316  random,
317  z,
318  p,
319  p_proj) == false)
320  return false;
321 
322  // transform the z component into the desired range [w1,w2)
323  z.z = z.z * w3 + w1 + w2;
324  }
325  else
326  {
327  // invert the glossy transmission component
328  if (invert_layer(
329  bsdf.glossy_trans(),
330  v_prev,
331  in,
332  out,
333  random,
334  z,
335  p,
336  p_proj) == false)
337  return false;
338 
339  // transform the z component into the desired range [w1,w2)
340  z.z = z.z * w4 + w1 + w2 + w3;
341  }
342 
343  // start accumulating all probabilities
344  p = 0.0f;
345  p_proj = 0.0f;
346 
347  // add the diffuse component
348  p += w1 * bsdf.diffuse().p(v_prev, in, out, cugar::kSolidAngle);
349  p_proj += w1 * bsdf.diffuse().p(v_prev, in, out, cugar::kProjectedSolidAngle);
350 
351  // add the glossy component
352  p += w2 * bsdf.glossy().p(v_prev, in, out, cugar::kSolidAngle);
353  p_proj += w2 * bsdf.glossy().p(v_prev, in, out, cugar::kProjectedSolidAngle);
354 
355  // add the transmission component
356  p += w3 * bsdf.diffuse_trans().p(v_prev, in, out, cugar::kSolidAngle);
357  p_proj += w3 * bsdf.diffuse_trans().p(v_prev, in, out, cugar::kProjectedSolidAngle);
358 
359  // add the glossy transmission component
360  p += w4 * bsdf.glossy_trans().p(v_prev, in, out, cugar::kSolidAngle);
361  p_proj += w4 * bsdf.glossy_trans().p(v_prev, in, out, cugar::kProjectedSolidAngle);
362 
363  // and invert the final sum
364  p = 1.0f / cugar::max( p, 1.0e-8f );
365  p_proj = 1.0f / cugar::max( p_proj, 1.0e-8f );
366 
367  return true;
368 }
369 
370 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
372  ComponentSelectionStrategy _strategy,
373  const Bsdf& bsdf,
374  const VertexGeometry& geom,
375  const cugar::Vector3f& in,
376  const cugar::Vector3f& out,
377  const bool _RR)
378 {
379  // save the strategy and RR choices
380  strategy = _strategy;
381  RR = _RR;
382 
383  // compute the forward sampling weights for all BSDF components
384  bsdf.sampling_weights(geom, in, w);
385 
386  if (RR == false)
387  {
388  const float w_sum = w[0] + w[1] + w[2] + w[3];
389 
390  for (uint32 i = 0; i < 4; ++i)
391  w[i] /= w_sum;
392  }
393 
394  // add the diffuse component
395  p_comp[DR] = bsdf.diffuse().p(geom, in, out, cugar::kSolidAngle);
396  p_comp_proj[DR] = bsdf.diffuse().p(geom, in, out, cugar::kProjectedSolidAngle);
397 
398  // add the glossy component
399  p_comp[GR] = bsdf.glossy().p(geom, in, out, cugar::kSolidAngle);
400  p_comp_proj[GR] = bsdf.glossy().p(geom, in, out, cugar::kProjectedSolidAngle);
401 
402  // add the transmission component
403  p_comp[DT] = bsdf.diffuse_trans().p(geom, in, out, cugar::kSolidAngle);
404  p_comp_proj[DT] = bsdf.diffuse_trans().p(geom, in, out, cugar::kProjectedSolidAngle);
405 
406  // add the glossy transmission component
407  p_comp[GT] = bsdf.glossy_trans().p(geom, in, out, cugar::kSolidAngle);
408  p_comp_proj[GT] = bsdf.glossy_trans().p(geom, in, out, cugar::kProjectedSolidAngle);
409 
410  if (strategy == BsdfInverse::kBrdfComponentSelection)
411  {
412  // evaluate f for all components
413  bsdf.f( geom, in, out, f_comp );
414  }
415 
416  precompute_component_selection_coefficients();
417 }
418 
419 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
420 bool BsdfInverse::precompute_component_selection_coefficients()
421 {
422  if (strategy == BsdfInverse::kUniformComponentSelection)
423  {
424  for (uint32 i = 0; i < 4; ++i)
425  p_sel[i] = 1.0f;
426  }
427  else if (strategy == BsdfInverse::kWeightedComponentSelection)
428  {
429  for (uint32 i = 0; i < 4; ++i)
430  p_sel[i] = w[i];
431  }
432  else if (strategy == BsdfInverse::kPdfComponentSelection)
433  {
434  for (uint32 i = 0; i < 4; ++i)
435  p_sel[i] = p_comp_proj[i];
436  }
437  else if (strategy == BsdfInverse::kWeightedPdfComponentSelection)
438  {
439  for (uint32 i = 0; i < 4; ++i)
440  p_sel[i] = w[i] * p_comp_proj[i];
441  }
442  else if (strategy == BsdfInverse::kBrdfComponentSelection)
443  {
444  for (uint32 i = 0; i < 4; ++i)
445  p_sel[i] = w[i] == 0 ? 0.0f : cugar::max_comp( f_comp[i] ) /*/ (w[i] * p_comp_proj[i])*/;
446  }
447 
448  // normalize the component selection pdf
449  const float p_sel_sum = p_sel[0] + p_sel[1] + p_sel[2] + p_sel[3];
450 
451  if (p_sel_sum)
452  {
453  for (uint32 i = 0; i < 4; ++i)
454  p_sel[i] /= p_sel_sum;
455 
456  return true;
457  }
458  return false;
459 }
460 
461 // sample a component given a random number
462 //
463 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
465 {
466  return
467  v < p_sel[DR] ? Bsdf::kDiffuseReflection :
468  v < p_sel[DR] + p_sel[GR] ? Bsdf::kGlossyReflection :
469  v < p_sel[DR] + p_sel[GR] + p_sel[DT] ? Bsdf::kDiffuseTransmission :
470  Bsdf::kGlossyTransmission;
471 }
472 
473 template <typename TRandomGenerator>
474 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
476  const Bsdf& bsdf,
477  const VertexGeometry& geom,
478  const cugar::Vector3f& in,
479  const cugar::Vector3f& out,
480  TRandomGenerator& random,
481  cugar::Vector3f& z,
482  BsdfInverse::PdfType pdf_type,
483  float* p,
484  float* p_proj)
485 {
486  // make sure this sample can be inverted
487  if (!cugar::is_finite(p_comp_proj[0]) ||
488  !cugar::is_finite(p_comp_proj[1]) ||
489  !cugar::is_finite(p_comp_proj[2]) ||
490  !cugar::is_finite(p_comp_proj[3]))
491  return false;
492 
493  // start accumulating all probabilities
494  if (p != NULL &&
495  p_proj != NULL)
496  {
497  *p = *p_proj = 0.0f;
498  for (uint32 i = 0; i < 4; ++i)
499  {
500  *p += w[i] * p_comp[i];
501  *p_proj += w[i] * p_comp_proj[i];
502  }
503 
504  if (pdf_type == BsdfInverse::kInverseTransformPdf)
505  {
506  // invert the final pdf sum to return the pdf of the inverse transform
507  *p = 1.0f / cugar::max( *p, 1.0e-8f );
508  *p_proj = 1.0f / cugar::max( *p_proj, 1.0e-8f );
509  }
510  }
511 
512  const float v = random.next();
513 
514  const Bsdf::ComponentType out_comp = sample_component( v );
515 
516  return invert_component( bsdf, geom, in, out, out_comp, random, z, true );
517 }
518 
519 template <typename TRandomGenerator>
520 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
522  const Bsdf& bsdf,
523  const VertexGeometry& geom,
524  const cugar::Vector3f& in,
525  const cugar::Vector3f& out,
526  const Bsdf::ComponentType out_comp,
527  TRandomGenerator& random,
528  cugar::Vector3f& z,
529  const bool output_global_coordinates)
530 {
531  float inv_p_comp;
532  float inv_p_comp_proj;
533 
534  if (out_comp == Bsdf::kDiffuseReflection)
535  {
536  // invert the diffuse component
537  if (invert_layer(
538  bsdf.diffuse(),
539  geom,
540  in,
541  out,
542  random,
543  z,
544  inv_p_comp,
545  inv_p_comp_proj) == false)
546  return false;
547 
548  // optionally replace z.z with the source random variable v, normalized to [0,1)
549  //z.z = v / p_sel[0];
550 
551  if (output_global_coordinates)
552  {
553  // transform the z component into the desired range [0,w1)
554  z.z = z.z * w[DR];
555  }
556  }
557  else if (out_comp == Bsdf::kGlossyReflection)
558  {
559  // invert the glossy component
560  if (invert_layer(
561  bsdf.glossy(),
562  geom,
563  in,
564  out,
565  random,
566  z,
567  inv_p_comp,
568  inv_p_comp_proj) == false)
569  return false;
570 
571  // optionally replace z.z with the source random variable v, normalized to [0,1)
572  //z.z = (v - p_sel[0]) / p_sel[1];
573 
574  if (output_global_coordinates)
575  {
576  // transform the z component into the desired range [w1,w2)
577  z.z = z.z * w[GR] + w[DR];
578  }
579  }
580  else if (out_comp == Bsdf::kDiffuseTransmission)
581  {
582  // invert the diffuse transmission component
583  if (invert_layer(
584  bsdf.diffuse_trans(),
585  geom,
586  in,
587  out,
588  random,
589  z,
590  inv_p_comp,
591  inv_p_comp_proj) == false)
592  return false;
593 
594  // optionally replace z.z with the source random variable v, normalized to [0,1)
595  //z.z = (v - p_sel[0] - p_sel[1]) / p_sel[2];
596 
597  if (output_global_coordinates)
598  {
599  // transform the z component into the desired range [w1,w2)
600  z.z = z.z * w[DT] + w[GR] + w[DR];
601  }
602  }
603  else
604  {
605  // invert the glossy transmission component
606  if (invert_layer(
607  bsdf.glossy_trans(),
608  geom,
609  in,
610  out,
611  random,
612  z,
613  inv_p_comp,
614  inv_p_comp_proj) == false)
615  return false;
616 
617  // optionally replace z.z with the source random variable v, normalized to [0,1)
618  //z.z = (v - p_sel[0] - p_sel[1] - p_sel[2]) / p_sel[3];
619 
620  if (output_global_coordinates)
621  {
622  // transform the z component into the desired range [w1,w2)
623  z.z = z.z * w[GT] + w[DT] + w[GR] + w[DR];
624  }
625  }
626  return true;
627 }
628 
629 // return the Jacobian determinant of the inverse transform for a given component;
630 // note that the returned Jacobian determinant is equal to the pdf of sampling that component, by definition of pdf of a transformed variable.
631 // In fact, if U is a uniform r.v. in the primary sample space, and X is the transformed path space variable, X = T(U), we have:
632 //
633 // P(X) = P(T_inv(X)) * |J[T_inv](X)| = P(U) * |J[T_inv](X)| = |J[T_inv](X)|
634 //
635 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
637  const Bsdf::ComponentType out_comp,
638  cugar::SphericalMeasure measure,
639  const bool weighted) const
640 {
641  return
642  (out_comp == Bsdf::kDiffuseReflection) ? (weighted ? w[DR] : 1.0f) * (measure == cugar::kSolidAngle ? p_comp[DR] : p_comp_proj[DR]) :
643  (out_comp == Bsdf::kGlossyReflection) ? (weighted ? w[GR] : 1.0f) * (measure == cugar::kSolidAngle ? p_comp[GR] : p_comp_proj[GR]) :
644  (out_comp == Bsdf::kDiffuseTransmission) ? (weighted ? w[DT] : 1.0f) * (measure == cugar::kSolidAngle ? p_comp[DT] : p_comp_proj[DT]) :
645  (weighted ? w[GT] : 1.0f) * (measure == cugar::kSolidAngle ? p_comp[GT] : p_comp_proj[GT]);
646 }
647 
648 // return the pdf with which a given component is selected by the inversion method.
649 //
650 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
652  const Bsdf::ComponentType out_comp) const
653 {
654  return
655  (out_comp == Bsdf::kDiffuseReflection) ? p_sel[DR] :
656  (out_comp == Bsdf::kGlossyReflection) ? p_sel[GR] :
657  (out_comp == Bsdf::kDiffuseTransmission) ? p_sel[DT] :
658  p_sel[GT];
659 }
660 
661 // return the sampling weight applied to a given component
662 //
663 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
665  const Bsdf::ComponentType out_comp) const
666 {
667  return
668  (out_comp == Bsdf::kDiffuseReflection) ? w[DR] :
669  (out_comp == Bsdf::kGlossyReflection) ? w[GR] :
670  (out_comp == Bsdf::kDiffuseTransmission) ? w[DT] :
671  w[GT];
672 }
673 
674 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
675 void inversion_pdf(
676  const Bsdf& bsdf,
677  const VertexGeometry& v_prev,
678  const cugar::Vector3f& in,
679  const cugar::Vector3f& out,
680  cugar::Vector3f& z,
681  float& p,
682  float& p_proj)
683 {
684  // choose the BSDF component utilized for inversion
685  float diffuse_refl_prob;
686  float diffuse_trans_prob;
687  float glossy_refl_prob;
688  float glossy_trans_prob;
689 
690  bsdf.sampling_weights(v_prev, in, diffuse_refl_prob, diffuse_trans_prob, glossy_refl_prob, glossy_trans_prob);
691 
692  float p1, p1_proj;
693  float p2, p2_proj;
694  float p3, p3_proj;
695  float p4, p4_proj;
696 
697  // invert the diffuse component
698  p1 = bsdf.diffuse().p(v_prev, in, out, cugar::kSolidAngle);
699  p1_proj = bsdf.diffuse().p(v_prev, in, out, cugar::kProjectedSolidAngle);
700 
701  p1 *= diffuse_refl_prob;
702  p1_proj *= diffuse_refl_prob;
703 
704  // invert the glossy component
705  p2 = bsdf.glossy().p(v_prev, in, out, cugar::kSolidAngle);
706  p2_proj = bsdf.glossy().p(v_prev, in, out, cugar::kProjectedSolidAngle);
707 
708  p2 *= glossy_refl_prob;
709  p2_proj *= glossy_refl_prob;
710 
711  // invert the diffuse transmission component
712  p3 = bsdf.diffuse_trans().p(v_prev, in, out, cugar::kSolidAngle);
713  p3_proj = bsdf.diffuse_trans().p(v_prev, in, out, cugar::kProjectedSolidAngle);
714 
715  p3 *= diffuse_trans_prob;
716  p3_proj *= diffuse_trans_prob;
717 
718  // invert the glossy transmission component
719  p4 = bsdf.glossy_trans().p(v_prev, in, out, cugar::kSolidAngle);
720  p4_proj = bsdf.glossy_trans().p(v_prev, in, out, cugar::kProjectedSolidAngle);
721 
722  p4 *= glossy_trans_prob;
723  p4_proj *= glossy_trans_prob;
724 
725  p = 1.0f / (p1 + p2 + p3 + p4);
726  p_proj = 1.0f / (p1_proj + p2_proj + p3_proj + p4_proj);
727 }
728 
729 template <typename PathType, typename TRandomGenerator>
730 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
731 bool invert_eye_subpath(const PathType& path, const uint32 t, const uint32 t_ext, float* out_Z, float* out_pdf, const RenderingContextView& renderer, TRandomGenerator& random)
732 {
733  VertexGeometry v_prev;
734  cugar::Vector3f in;
735 
736  *out_pdf = 1.0f;
737 
738  if (t == 0)
739  {
740  // special case: this is a light path which hit the camera, and we need to invert the lens sampling
741  return false;
742  }
743  else
744  {
745  // setup vertex t - 1
746  setup_differential_geometry(renderer.mesh, path.v_E(t - 1), &v_prev);
747 
748  // setup the incoming edge at t - 1
749  if (t == 1)
750  in = cugar::Vector3f(0.0f);
751  else if (t == 2)
752  {
753  // vertex zero is represented by uv's on the lens (currently a pinhole)
754  in = cugar::normalize(cugar::Vector3f(renderer.camera.eye) - v_prev.position);
755  }
756  else
757  {
758  // fetch vertex t - 2
759  const cugar::Vector3f p_prev2 = interpolate_position(renderer.mesh, path.v_E(t - 2));
760 
761  in = cugar::normalize(p_prev2 - v_prev.position);
762  }
763  }
764 
765  // NOTE: here we can basically assume t is at least 2, otherwise, if t == 1 we would have to consider the camera sampler instead of the BSDF at i = 1
766  // (corresponding to pure light tracing)
767  for (uint32 i = t; i < t_ext; ++i)
768  {
769  VertexGeometryId v_id = path.v_E(i);
770  VertexGeometry v;
771 
772  setup_differential_geometry(renderer.mesh, v_id, &v);
773 
774  // invert the edge (v_prev -> v)
775  //
776 
777  // 1. build the BSDF at v_prev
778 
779  // fetch the material at v_prev
780  const int material_id = renderer.mesh.material_indices[path.v_E(i-1).prim_id];
781  MeshMaterial material = renderer.mesh.materials[material_id];
782 
783  // perform all texture lookups
784  material.diffuse *= texture_lookup(v_prev.texture_coords, material.diffuse_map, renderer.textures, cugar::Vector4f(1.0f));
785  material.specular *= texture_lookup(v_prev.texture_coords, material.specular_map, renderer.textures, cugar::Vector4f(1.0f));
786  material.emissive *= texture_lookup(v_prev.texture_coords, material.emissive_map, renderer.textures, cugar::Vector4f(1.0f));
787 
788  Bsdf bsdf(kRadianceTransport, renderer, material);
789 
790  // 2. setup the outgoing edge
791  cugar::Vector3f out = v.position - v_prev.position;
792 
793  const float d2 = cugar::max( cugar::square_length(out), 1.0e-8f );
794 
795  out /= sqrtf(d2); // normalize it
796 
797  // 3. invert the bsdf
798  cugar::Vector3f z;
799  float p, p_proj;
800 
801  if (invert_bsdf(
802  bsdf,
803  v_prev,
804  in,
805  out,
806  random,
807  z,
808  p,
809  p_proj) == false)
810  return false;
811 
812  // store the recovered primary space coordinates
813  out_Z[(i - t) * 3 + 0] = z.x;
814  out_Z[(i - t) * 3 + 1] = z.y;
815  out_Z[(i - t) * 3 + 2] = z.z;
816 
817  // multiply by the inverse of the geometric term G(v_prev,v)
818  *out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
819  //*out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
820 
821  // curry the previous vertex and incoming edge information to the next iteration
822  v_prev = v;
823  in = -out;
824  }
825  return true;
826 }
827 
828 template <typename PathType>
829 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
830 float eye_subpath_inversion_pdf(const PathType& path, const uint32 t, const uint32 t_ext, const float* out_Z, const RenderingContextView& renderer)
831 {
832  FERMAT_ASSERT(t >= 2);
833  FERMAT_ASSERT(t_ext <= path.n_vertices);
834 
835  VertexGeometry v_prev;
836  cugar::Vector3f in;
837 
838  float out_pdf = 1.0f;
839 
840  if (t == 0)
841  {
842  // special case: this is a light path which hit the camera, and we need to invert the lens sampling
843  return 0.0f;
844  }
845  else
846  {
847  // setup vertex t - 1
848  setup_differential_geometry(renderer.mesh, path.v_E(t - 1), &v_prev);
849 
850  // setup the incoming edge at t - 1
851  if (t == 1)
852  in = cugar::Vector3f(0.0f);
853  else if (t == 2)
854  {
855  // vertex zero is represented by uv's on the lens (currently a pinhole)
856  in = cugar::normalize(cugar::Vector3f(renderer.camera.eye) - v_prev.position);
857  }
858  else
859  {
860  // fetch vertex t - 2
861  const cugar::Vector3f p_prev2 = interpolate_position(renderer.mesh, path.v_E(t - 2));
862 
863  in = cugar::normalize(p_prev2 - v_prev.position);
864  }
865  }
866 
867  // NOTE: here we can basically assume t is at least 2, otherwise, if t == 1 we would have to consider the camera sampler instead of the BSDF at i = 1
868  // (corresponding to pure light tracing)
869  for (uint32 i = t; i < t_ext; ++i)
870  {
871  VertexGeometryId v_id = path.v_E(i);
872  VertexGeometry v;
873 
874  setup_differential_geometry(renderer.mesh, v_id, &v);
875 
876  // invert the edge (v_prev -> v)
877  //
878 
879  // 1. build the BSDF at v_prev
880 
881  // fetch the material at v_prev
882  const int material_id = renderer.mesh.material_indices[path.v_E(i - 1).prim_id];
883  MeshMaterial material = renderer.mesh.materials[material_id];
884 
885  // perform all texture lookups
886  material.diffuse *= texture_lookup(v_prev.texture_coords, material.diffuse_map, renderer.textures, cugar::Vector4f(1.0f));
887  material.specular *= texture_lookup(v_prev.texture_coords, material.specular_map, renderer.textures, cugar::Vector4f(1.0f));
888  material.emissive *= texture_lookup(v_prev.texture_coords, material.emissive_map, renderer.textures, cugar::Vector4f(1.0f));
889 
890  Bsdf bsdf(kRadianceTransport, renderer, material);
891 
892  // 2. setup the outgoing edge
893  cugar::Vector3f out = v.position - v_prev.position;
894 
895  const float d2 = cugar::max(cugar::square_length(out), 1.0e-8f);
896 
897  out /= sqrtf(d2); // normalize it
898 
899  // 3. compute the inversion pdf
900  cugar::Vector3f z;
901  float p, p_proj;
902 
903  // fetch the recovered primary space coordinates
904  z.x = out_Z[(i - t) * 3 + 0];
905  z.y = out_Z[(i - t) * 3 + 1];
906  z.z = out_Z[(i - t) * 3 + 2];
907 
908  inversion_pdf(
909  bsdf,
910  v_prev,
911  in,
912  out,
913  z,
914  p,
915  p_proj);
916 
917  // multiply by the inverse of the geometric term G(v_prev,v)
918  out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
919  //out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
920 
921  // curry the previous vertex and incoming edge information to the next iteration
922  v_prev = v;
923  in = -out;
924  }
925  return out_pdf;
926 }
927 
928 template <typename PathType, typename TRandomGenerator>
929 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
930 bool invert_light_subpath(const PathType& path, const uint32 s, const uint32 s_ext, float* out_Z, float* out_pdf, const RenderingContextView& renderer, TRandomGenerator& random)
931 {
932  FERMAT_ASSERT(s_ext <= path.n_vertices);
933 
934  VertexGeometry v_prev;
935  cugar::Vector3f in;
936 
937  *out_pdf = 1.0f;
938 
939  if (s == 0)
940  {
941  float z[3] = { random.next(), random.next(), random.next() };
942 
943  // special case: this is a eye path which hit the light, and we need to invert the light source sampling
944  if (renderer.mesh_light.invert_impl(path.v_L(0).prim_id, path.v_L(0).uv, z, out_Z, out_pdf) == false)
945  return false;
946 
947  setup_differential_geometry(renderer.mesh, path.v_L(0), &v_prev);
948 
949  in = cugar::Vector3f(0.0f);
950  }
951  else
952  {
953  // setup vertex s - 1
954  setup_differential_geometry(renderer.mesh, path.v_L(s - 1), &v_prev);
955 
956  // setup the incoming edge at s - 1
957  if (s == 1)
958  in = cugar::Vector3f(0.0f);
959  else
960  {
961  // fetch vertex s - 2
962  const cugar::Vector3f p_prev2 = interpolate_position( renderer.mesh, path.v_L(s - 2) );
963 
964  in = cugar::normalize(p_prev2 - v_prev.position);
965  }
966  }
967 
968  if (s <= 1 && s_ext >= 2)
969  {
970  // invert the EDF at the light source
971  VertexGeometryId v_id = path.v_L(1);
972  VertexGeometry v;
973 
974  setup_differential_geometry(renderer.mesh, v_id, &v);
975 
976  // invert the edge (v_prev -> v)
977  //
978 
979  // 1. build the EDF at v_prev
980 
981  // fetch the material at v_prev
982  const int material_id = renderer.mesh.material_indices[path.v_L(0).prim_id];
983  MeshMaterial material = renderer.mesh.materials[material_id];
984 
985  // NOTE: we don't interpolate textures here because, for the currently used diffuse EDF, the probability is not affected by the actual material parameters
986  Edf edf(material);
987 
988  // 2. setup the outgoing edge
989  cugar::Vector3f out = v.position - v_prev.position;
990 
991  const float d2 = cugar::max(cugar::square_length(out), 1.0e-8f);
992 
993  out /= sqrtf(d2); // normalize it
994 
995  // 3. invert the EDF
996  cugar::Vector3f z;
997  float p, p_proj;
998 
999  edf.invert(
1000  v_prev,
1001  in,
1002  out,
1003  random,
1004  z,
1005  p,
1006  p_proj);
1007 
1008  // store the recovered primary space coordinates
1009  out_Z[(1 - s) * 3 + 0] = z.x;
1010  out_Z[(1 - s) * 3 + 1] = z.y;
1011  out_Z[(1 - s) * 3 + 2] = z.z;
1012 
1013  // multiply by the inverse of the geometric term G(v_prev,v)
1014  *out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
1015  //*out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
1016 
1017  // curry the previous vertex and incoming edge information to the next iteration
1018  v_prev = v;
1019  in = -out;
1020  }
1021 
1022  for (uint32 i = cugar::max( s, 2u ); i < s_ext; ++i)
1023  {
1024  FERMAT_ASSERT(path.v_L(i).prim_id < renderer.mesh.num_triangles);
1025  VertexGeometryId v_id = path.v_L(i);
1026  VertexGeometry v;
1027 
1028  setup_differential_geometry(renderer.mesh, v_id, &v);
1029 
1030  // invert the edge (v_prev -> v)
1031  //
1032 
1033  // 1. build the BSDF at v_prev
1034 
1035  // fetch the material at v_prev
1036  FERMAT_ASSERT(path.v_L(i - 1).prim_id < renderer.mesh.num_triangles);
1037  const int material_id = renderer.mesh.material_indices[path.v_L(i - 1).prim_id];
1038  FERMAT_ASSERT(material_id >= 0 && material_id < renderer.mesh.num_materials);
1039  MeshMaterial material = renderer.mesh.materials[material_id];
1040 
1041  // perform all texture lookups
1042  material.diffuse *= texture_lookup(v_prev.texture_coords, material.diffuse_map, renderer.textures, cugar::Vector4f(1.0f));
1043  material.specular *= texture_lookup(v_prev.texture_coords, material.specular_map, renderer.textures, cugar::Vector4f(1.0f));
1044  material.emissive *= texture_lookup(v_prev.texture_coords, material.emissive_map, renderer.textures, cugar::Vector4f(1.0f));
1045 
1046  Bsdf bsdf(kParticleTransport, renderer, material);
1047 
1048  // 2. setup the outgoing edge
1049  cugar::Vector3f out = v.position - v_prev.position;
1050 
1051  const float d2 = cugar::max(cugar::square_length(out), 1.0e-8f);
1052 
1053  out /= sqrtf(d2); // normalize it
1054 
1055  // 3. invert the bsdf
1056  cugar::Vector3f z;
1057  float p, p_proj;
1058 
1059  if (invert_bsdf(
1060  bsdf,
1061  v_prev,
1062  in,
1063  out,
1064  random,
1065  z,
1066  p,
1067  p_proj) == false)
1068  return false;
1069 
1070  // store the recovered primary space coordinates
1071  out_Z[(i - s) * 3 + 0] = z.x;
1072  out_Z[(i - s) * 3 + 1] = z.y;
1073  out_Z[(i - s) * 3 + 2] = z.z;
1074 
1075  // multiply by the inverse of the geometric term G(v_prev,v)
1076  *out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
1077  //*out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
1078 
1079  // curry the previous vertex and incoming edge information to the next iteration
1080  v_prev = v;
1081  in = -out;
1082  }
1083  return true;
1084 }
1085 
1086 template <typename PathType>
1087 FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
1088 float light_subpath_inversion_pdf(const PathType& path, const uint32 s, const uint32 s_ext, const float* out_Z, const RenderingContextView& renderer)
1089 {
1090  FERMAT_ASSERT(s_ext <= path.n_vertices);
1091 
1092  VertexGeometry v_prev;
1093  cugar::Vector3f in;
1094 
1095  float out_pdf = 1.0f;
1096 
1097  if (s == 0)
1098  {
1099  // special case: this is a eye path which hit the light, and we need to invert the light source sampling
1100  out_pdf = renderer.mesh_light.inverse_pdf_impl(path.v_L(0).prim_id, path.v_L(0).uv, out_Z);
1101 
1102  setup_differential_geometry(renderer.mesh, path.v_L(0), &v_prev);
1103 
1104  in = cugar::Vector3f(0.0f);
1105  }
1106  else
1107  {
1108  // setup vertex s - 1
1109  setup_differential_geometry(renderer.mesh, path.v_L(s - 1), &v_prev);
1110 
1111  // setup the incoming edge at s - 1
1112  if (s == 1)
1113  in = cugar::Vector3f(0.0f);
1114  else
1115  {
1116  // fetch vertex s - 2
1117  const cugar::Vector3f p_prev2 = interpolate_position(renderer.mesh, path.v_L(s - 2));
1118 
1119  in = cugar::normalize(p_prev2 - v_prev.position);
1120  }
1121  }
1122 
1123  if (s <= 1 && s_ext >= 2)
1124  {
1125  // invert the EDF at the light source
1126  VertexGeometryId v_id = path.v_L(1);
1127  VertexGeometry v;
1128 
1129  setup_differential_geometry(renderer.mesh, v_id, &v);
1130 
1131  // invert the edge (v_prev -> v)
1132  //
1133 
1134  // 1. build the EDF at v_prev
1135 
1136  // fetch the material at v_prev
1137  const int material_id = renderer.mesh.material_indices[path.v_L(0).prim_id];
1138  MeshMaterial material = renderer.mesh.materials[material_id];
1139 
1140  // NOTE: we don't interpolate textures here because, for the currently used diffuse EDF, the probability is not affected by the material parameters
1141  Edf edf(material);
1142 
1143  // 2. setup the outgoing edge
1144  cugar::Vector3f out = v.position - v_prev.position;
1145 
1146  const float d2 = cugar::max(cugar::square_length(out), 1.0e-8f);
1147 
1148  out /= sqrtf(d2); // normalize it
1149 
1150  // 3. invert the EDF
1151  cugar::Vector3f z;
1152  float p, p_proj;
1153 
1154  // fetch the recovered primary space coordinates
1155  z.x = out_Z[(1 - s) * 3 + 0];
1156  z.y = out_Z[(1 - s) * 3 + 1];
1157  z.z = out_Z[(1 - s) * 3 + 2];
1158 
1159  edf.inverse_pdf(
1160  v_prev,
1161  in,
1162  out,
1163  z,
1164  p,
1165  p_proj);
1166 
1167  // multiply by the inverse of the geometric term G(v_prev,v)
1168  out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
1169  //out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
1170 
1171  // curry the previous vertex and incoming edge information to the next iteration
1172  v_prev = v;
1173  in = -out;
1174  }
1175 
1176  for (uint32 i = cugar::max(s, 2u); i < s_ext; ++i)
1177  {
1178  VertexGeometryId v_id = path.v_L(i);
1179  VertexGeometry v;
1180 
1181  setup_differential_geometry(renderer.mesh, v_id, &v);
1182 
1183  // invert the edge (v_prev -> v)
1184  //
1185 
1186  // 1. build the BSDF at v_prev
1187 
1188  // fetch the material at v_prev
1189  const int material_id = renderer.mesh.material_indices[path.v_L(i - 1).prim_id];
1190  MeshMaterial material = renderer.mesh.materials[material_id];
1191 
1192  // perform all texture lookups
1193  material.diffuse *= texture_lookup(v_prev.texture_coords, material.diffuse_map, renderer.textures, cugar::Vector4f(1.0f));
1194  material.specular *= texture_lookup(v_prev.texture_coords, material.specular_map, renderer.textures, cugar::Vector4f(1.0f));
1195  material.emissive *= texture_lookup(v_prev.texture_coords, material.emissive_map, renderer.textures, cugar::Vector4f(1.0f));
1196 
1197  Bsdf bsdf(kParticleTransport, renderer, material);
1198 
1199  // 2. setup the outgoing edge
1200  cugar::Vector3f out = v.position - v_prev.position;
1201 
1202  const float d2 = cugar::max(cugar::square_length(out), 1.0e-8f);
1203 
1204  out /= sqrtf(d2); // normalize it
1205 
1206  // 3. compute the inversion pdf
1207  cugar::Vector3f z;
1208  float p, p_proj;
1209 
1210  // fetch the recovered primary space coordinates
1211  z.x = out_Z[(i - s) * 3 + 0];
1212  z.y = out_Z[(i - s) * 3 + 1];
1213  z.z = out_Z[(i - s) * 3 + 2];
1214 
1215  inversion_pdf(
1216  bsdf,
1217  v_prev,
1218  in,
1219  out,
1220  z,
1221  p,
1222  p_proj);
1223 
1224  // multiply by the inverse of the geometric term G(v_prev,v)
1225  out_pdf *= p_proj * d2 / cugar::max(fabsf(dot(v_prev.normal_s, out)*dot(v.normal_s, out)), 1.0e-8f);
1226  //out_pdf *= p * d2 / cugar::max( fabsf( dot(v.normal_s,out) ), 1.0e-8f);
1227 
1228  // curry the previous vertex and incoming edge information to the next iteration
1229  v_prev = v;
1230  in = -out;
1231  }
1232  return out_pdf;
1233 }
1234 
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert_trans.h:103
SphericalMeasure
Definition: differential_geometry.h:50
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE float pdf(const Bsdf::ComponentType out_comp, cugar::SphericalMeasure measure=cugar::kProjectedSolidAngle, const bool weighted=false) const
Definition: path_inversion.h:636
Definition: vertex.h:105
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: ggx_smith.h:484
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE float pdf_comp(const Bsdf::ComponentType out_comp) const
Definition: path_inversion.h:651
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE float weight(const Bsdf::ComponentType out_comp) const
Definition: path_inversion.h:664
FERMAT_HOST_DEVICE const glossy_component & glossy_trans() const
Definition: bsdf.h:293
ComponentType
Definition: bsdf.h:139
Definition: MeshView.h:55
FERMAT_HOST_DEVICE const diffuse_component & diffuse() const
Definition: bsdf.h:283
Definition: vertex.h:92
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE Bsdf::ComponentType sample_component(const float v) const
Definition: path_inversion.h:464
FERMAT_HOST_DEVICE const diffuse_trans_component & diffuse_trans() const
Definition: bsdf.h:278
FERMAT_HOST_DEVICE void setup_differential_geometry(const MeshView &mesh, const uint32 tri_id, const float u, const float v, VertexGeometry *geom, float *pdf=0)
Definition: mesh_utils.h:185
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE bool invert(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, RandomGeneratorT &random, Vector3f &z, float &p, float &p_proj) const
Definition: lambert_edf.h:113
float random()
Definition: tiled_sampling.h:44
Definition: path_inversion.h:59
FERMAT_HOST_DEVICE float inverse_pdf_impl(const uint32_t prim_id, const cugar::Vector2f &uv, const float *out_Z) const
Definition: lights.h:465
FERMAT_HOST_DEVICE bool invert_impl(const uint32_t prim_id, const cugar::Vector2f &uv, const float *in_Z, float *out_Z, float *out_pdf) const
Definition: lights.h:436
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void sampling_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, float &diffuse_refl_prob, float &diffuse_trans_prob, float &glossy_refl_prob, float &glossy_trans_prob) const
Definition: bsdf.h:530
Definition: edf.h:49
Definition: bsdf.h:123
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void inverse_pdf(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const Vector3f u, float &p, float &p_proj) const
Definition: lambert_edf.h:143
FERMAT_HOST_DEVICE cugar::Vector3f interpolate_position(const MeshView &mesh, const uint32 tri_id, const float u, const float v, float *pdf=0)
Definition: mesh_utils.h:323
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE cugar::Vector3f f(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, const ComponentType components=kAllComponents) const
Definition: bsdf.h:312
Definition: renderer_view.h:80
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE bool invert_component(const Bsdf &bsdf, const VertexGeometry &geom, const cugar::Vector3f &in, const cugar::Vector3f &out, const Bsdf::ComponentType out_comp, TRandomGenerator &random, cugar::Vector3f &z, const bool output_global_coordinates=true)
Definition: path_inversion.h:521
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert.h:116
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE void setup(ComponentSelectionStrategy _strategy, const Bsdf &bsdf, const VertexGeometry &geom, const cugar::Vector3f &in, const cugar::Vector3f &out, const bool _RR=false)
Definition: path_inversion.h:371
FERMAT_HOST_DEVICE const glossy_component & glossy() const
Definition: bsdf.h:288
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE bool invert(const Bsdf &bsdf, const VertexGeometry &geom, const cugar::Vector3f &in, const cugar::Vector3f &out, TRandomGenerator &random, cugar::Vector3f &z, BsdfInverse::PdfType pdf_type=BsdfInverse::kDirectTransformPdf, float *p=NULL, float *p_proj=NULL)
Definition: path_inversion.h:475