Fermat
mlt_core.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 #define DISABLE_SPHERICAL_PERTURBATIONS 0
32 
33 #define SPHERICAL_SCREEN_PERTURBATIONS 0
34 #define DISK_SCREEN_SPACE_PERTURBATION 1
35 
36 #include <mlt.h>
37 #include <mlt_perturbations.h>
38 #include <renderer.h>
39 #include <cugar/basic/timer.h>
40 #include <cugar/basic/cuda/timer.h>
41 #include <cugar/basic/primitives.h>
43 #include <cugar/color/rgbe.h>
45 #include <bsdf.h>
46 #include <edf.h>
47 #include <bpt_utils.h>
48 #include <bpt_context.h>
49 #include <bpt_control.h>
50 #include <bpt_samplers.h>
51 #include <tiled_sampling.h>
52 #include <path_inversion.h>
53 
54 
55 namespace {
56 
57  FERMAT_HOST_DEVICE
58  bool valid_sample(const float3 f)
59  {
60  return
61  cugar::is_finite(f.x) && cugar::is_finite(f.y) && cugar::is_finite(f.z) &&
62  !cugar::is_nan(f.x) && !cugar::is_nan(f.y) && !cugar::is_nan(f.z);
63  }
64 
65  FERMAT_HOST_DEVICE
66  bool valid_non_zero_sample(const float3 f)
67  {
68  return
69  cugar::max_comp( f ) > 0.0f &&
70  valid_sample( f );
71  }
72 
73  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
74  cugar::Vector3f reconstruct_input_edge(const VertexGeometryId v_prev, const VertexGeometryId v, const RenderingContextView& renderer)
75  {
76  return
77  interpolate_position(renderer.mesh, v_prev) -
78  interpolate_position(renderer.mesh, v);
79  }
80 
81  template <typename PathVertexType>
82  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
83  PathVertexType reconstruct_vertex(const VertexGeometryId v_prev, const VertexGeometryId v, const uint32 in_bounce, const RenderingContextView& renderer)
84  {
85  const cugar::Vector3f old_in = reconstruct_input_edge( v_prev, v, renderer );
86  return PathVertexType( old_in, v, cugar::Vector3f(0.0f), TempPathWeights(), in_bounce, renderer );
87  }
88 
89  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
90  float cos_theta(const cugar::Vector3f& N, const cugar::Vector3f dir) { return fabsf( dot(N, dir) ); }
91 
92  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
93  float cos_theta(const VertexGeometry& geom, const cugar::Vector3f dir) { return fabsf( dot(geom.normal_s, dir) ); }
94 
95 } // anonymous namespace
96 
97 
98 struct MLTContext : BPTContextBase<MLTOptions>
99 {
100  TiledSequenceView sequence;
101  uint32 n_light_paths; // number of light paths
102  uint32 n_eye_paths; // number of eye paths
103  float4* connections_value;
104  uint4* connections_index;
105  uint32* connections_counter;
106  float* st_norms;
107  float* st_norms_cdf;
108  uint32* st_counters;
109  uint32* seeds;
110  char4* st;
111  VertexGeometryId* bpt_light_vertices; // BPT light vertices
112  VertexGeometryId* bpt_eye_vertices; // BPT eye vertices
113  VertexGeometryId* mut_vertices;
114  VertexGeometryId* vertices;
115  float4* path_value;
116  float* path_pdf;
117  uint32* rejections;
118  uint32 n_chains;
119  uint32 chain_length;
120  uint32 chain_step;
121  float pdf_norm;
122  uint32 enable_mutations;
123  float* acceptance_rate_sum;
124  float* checksum;
125 
126  float4* mut_f_vertices;
127  float4* f_vertices;
128  cugar::Vector4f* Q_old;
129  cugar::Vector4f* Q_new;
130 
131  MeshLight mesh_light;
132 
133  MLTContext() {}
134 
135  MLTContext(
136  MLT& _mlt,
137  const RenderingContextView& _renderer) :
139  _renderer,
140  _mlt.m_light_vertices.view(),
141  _mlt.m_queues.view(_mlt.m_n_init_paths, _mlt.m_n_init_paths),
142  _mlt.m_options ),
143  sequence(_mlt.m_sequence.view()),
144  mesh_light( options.use_vpls ? _renderer.mesh_vpls : _renderer.mesh_light )
145  {}
146 
147  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
148  float u_m(const uint32 chain_id, const uint32 dim) const { return cugar::randfloat( dim, chain_id + n_chains * chain_step ); }
149 
150  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
151  void discard(const uint32 chain_id)
152  {
153  Q_old[chain_id] = 1.0f;
154  Q_new[chain_id] = 0.0f;
155  }
156 
157  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
158  bool discard_invalid(const uint32 chain_id)
159  {
160  if (!valid_sample( Q_old[chain_id].xyz() ) ||
161  !valid_sample( Q_new[chain_id].xyz() ))
162  {
163  discard( chain_id );
164  return true;
165  }
166  return false;
167  }
168 };
169 
170 namespace { // anonymous namespace
171 
172  // A config for the BPT presampling/seeding pass, used to store all generated path vertices
173  //
174  struct MLTPresamplingBPTConfig : BPTConfigBase
175  {
176  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
177  MLTPresamplingBPTConfig(MLTContext& _context) :
179  _context.options,
180  VertexSampling::kAll,
181  VertexOrdering::kRandomOrdering,
182  VertexSampling::kAll,
183  true) {}
184 
185  // store the given light vertex
186  //
187  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
188  void visit_light_vertex(
189  const uint32 light_path_id,
190  const uint32 depth,
191  const VertexGeometryId v_id,
192  MLTContext& context,
193  const RenderingContextView& renderer) const
194  {
195  if (context.bpt_light_vertices)
196  context.bpt_light_vertices[light_path_id + depth * context.n_light_paths] = v_id;
197  }
198 
199  // store the given eye vertex
200  //
201  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
202  void visit_eye_vertex(
203  const uint32 eye_path_id,
204  const uint32 depth,
205  const VertexGeometryId v_id,
206  const EyeVertex& v,
207  MLTContext& context,
208  const RenderingContextView& renderer) const
209  {
210  if (context.bpt_eye_vertices)
211  context.bpt_eye_vertices[eye_path_id + depth * context.n_eye_paths] = v_id;
212  }
213  };
214 
215  //
216  // The \ref SampleSinkAnchor "Sample Sink" used by the BPT presampling/seeding pass
217  //
218  struct ConnectionsSink : SampleSinkBase
219  {
220  FERMAT_HOST_DEVICE
221  ConnectionsSink() {}
222 
223  FERMAT_HOST_DEVICE
224  void sink(
225  const uint32 channel,
226  const cugar::Vector4f value,
227  const uint32 light_path_id,
228  const uint32 eye_path_id,
229  const uint32 s,
230  const uint32 t,
231  MLTContext& context,
232  RenderingContextView& renderer)
233  {
234  if (cugar::max_comp(value.xyz()) > 0.0f)
235  {
236  const uint32 slot = cugar::atomic_add(context.connections_counter, 1);
237  context.connections_value[slot] = value;
238  context.connections_index[slot] = make_uint4(light_path_id, eye_path_id, s, t);
239 
240  cugar::atomic_add(context.st_norms + s + t * (context.options.max_path_length + 2), cugar::max_comp(value.xyz()));
241  }
242  }
243  };
244 
245  struct DecorrelatedRandoms
246  {
247  FERMAT_HOST_DEVICE
248  DecorrelatedRandoms(const uint32 _dim, const uint32 _seed) : dim(_dim), seed(_seed) {}
249 
250  FERMAT_HOST_DEVICE
251  float next()
252  {
253  return cugar::randfloat( dim++, seed % 65535u );
254  }
255 
256  uint32 dim;
257  uint32 seed;
258  };
259 
260  FERMAT_DEVICE FERMAT_FORCEINLINE
261  void accept_reject_accumulate(const uint32 chain_id, const uint32 s, const uint32 t, const cugar::Vector4f w, MLTContext& context, RenderingContextView& renderer)
262  {
263  // perform the MH acceptance-rejection test
264  const float new_pdf = cugar::max_comp(w.xyz());
265  const float old_pdf = context.path_pdf[chain_id];
266 
267  FERMAT_ASSERT( cugar::is_finite(old_pdf) && !cugar::is_nan(old_pdf) );
268  FERMAT_ASSERT( cugar::is_finite(new_pdf) && !cugar::is_nan(new_pdf) );
269  FERMAT_ASSERT( old_pdf >= 0.0f );
270  FERMAT_ASSERT( new_pdf >= 0.0f );
271 
272  const Path old_path(s + t, context.vertices + chain_id, context.n_chains);
273  const Path new_path(s + t, context.mut_vertices + chain_id, context.n_chains);
274 
275  const cugar::Vector2f old_uv = old_path.v_E(0).uv;
276  const cugar::Vector2f new_uv = new_path.v_E(0).uv;
277 
278  const uint32 old_pixel_x = cugar::quantize(old_uv.x, renderer.res_x);
279  const uint32 old_pixel_y = cugar::quantize(old_uv.y, renderer.res_y);
280  const uint32 old_pixel = old_pixel_x + old_pixel_y*renderer.res_x;
281 
282  const uint32 new_pixel_x = cugar::quantize(new_uv.x, renderer.res_x);
283  const uint32 new_pixel_y = cugar::quantize(new_uv.y, renderer.res_y);
284  const uint32 new_pixel = new_pixel_x + new_pixel_y*renderer.res_x;
285 
286  //const float T_ratio = w.w;
287  //const float ar = old_pdf ? fminf(1.0f, T_ratio * (new_pdf / old_pdf)) : (new_pdf ? 1.0f : 0.0f);
288  //FERMAT_ASSERT( cugar::is_finite(T_ratio) && !cugar::is_nan(T_ratio) );
289  //FERMAT_ASSERT( T_ratio >= 0.0f );
290  const float Q_old = cugar::max_comp( context.Q_old[chain_id].xyz() );
291  const float Q_new = cugar::max_comp( context.Q_new[chain_id].xyz() );
292  const float ar = Q_old ? cugar::min( 1.0f, Q_new / Q_old ) : (Q_new ? 1.0f : 0.0f);
293  FERMAT_ASSERT( cugar::is_finite(Q_old) && !cugar::is_nan(Q_old) );
294  FERMAT_ASSERT( cugar::is_finite(Q_new) && !cugar::is_nan(Q_new) );
295  FERMAT_ASSERT( cugar::is_finite(ar) && !cugar::is_nan(ar) );
296  FERMAT_ASSERT( ar >= 0.0f );
297 
298  if (old_pdf > 0)
299  {
300  const float4 old_value = context.path_value[chain_id];
301 
302  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, old_pixel).x, context.pdf_norm * (1.0f - ar) * (old_value.x / old_pdf) / float(renderer.instance + 1));
303  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, old_pixel).y, context.pdf_norm * (1.0f - ar) * (old_value.y / old_pdf) / float(renderer.instance + 1));
304  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, old_pixel).z, context.pdf_norm * (1.0f - ar) * (old_value.z / old_pdf) / float(renderer.instance + 1));
305  }
306  if (new_pdf > 0)
307  {
308  const float4 new_value = w;
309 
310  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, new_pixel).x, context.pdf_norm * ar * (new_value.x / new_pdf) / float(renderer.instance + 1));
311  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, new_pixel).y, context.pdf_norm * ar * (new_value.y / new_pdf) / float(renderer.instance + 1));
312  atomicAdd(&renderer.fb(FBufferDesc::COMPOSITED_C, new_pixel).z, context.pdf_norm * ar * (new_value.z / new_pdf) / float(renderer.instance + 1));
313  }
314 
315  if (context.u_m(chain_id, (context.options.max_path_length + 1) * 3) < ar)
316  {
317  context.path_value[chain_id] = w;
318  context.path_pdf[chain_id] = new_pdf;
319 
320  // copy the compact vertex information
321  for (uint32 i = 0; i < s + t; ++i)
322  {
323  context.vertices[chain_id + i * context.n_chains] = context.mut_vertices[chain_id + i * context.n_chains];
324  context.f_vertices[chain_id + i * context.n_chains] = context.mut_f_vertices[chain_id + i * context.n_chains];
325  }
326 
327  context.rejections[chain_id] = 0;
328  }
329  else
330  {
331  context.rejections[chain_id]++;
332 
333  //if ((context.rejections[chain_id] % 5) == 0)
334  // printf("chain[%u,%u:%u] stuck for %u iterations\n", s, t, chain_id, context.rejections[chain_id]);
335  }
336 
337  // keep some stats
338  cugar::atomic_add( context.acceptance_rate_sum, ar );
339 
340  // keep track of a checksum
341  cugar::atomic_add( context.checksum, context.path_pdf[chain_id] );
342  }
343 
344  //------------------------------------------------------------------------------
345 
346  FERMAT_DEVICE
347  bool perturb_primary_light_vertex(MLTContext& context, RenderingContextView& renderer, RayQueue& scatter_queue, const uint32 chain_id)
348  {
349  // fetch the original (s,t) pair
350  const uint32 s_old = context.st[chain_id].x;
351  const uint32 t_old = context.st[chain_id].y;
352 
353  // and generate a new pair
354  const uint32 t = (context.enable_mutations == false) || (context.options.st_perturbations == false) || (s_old == 0) ?
355  t_old :
356  2 + cugar::quantize(context.u_m(chain_id, 0), s_old + t_old - 2); // t for now is at least 2
357  const uint32 s = s_old + t_old - t;
358  FERMAT_ASSERT(s + t == s_old + t_old);
359 
360  // store the new (s,t) pair
361  context.st[chain_id] = make_char4(s_old, t_old, s, t);
362 
363  // set up the acceptance ratios
364  context.Q_old[chain_id] = cugar::Vector4f(1.0f);
365  context.Q_new[chain_id] = cugar::Vector4f(1.0f);
366 
367  // check if the light subpath stops here
368  if (s == 0)
369  {
370  context.light_vertices.vertex_path_id[chain_id] = uint32(-1);
371  context.light_vertices.vertex_weights[chain_id] = PathWeights(1.0f, 0.0f); // track the acceptance rate factors
372  return true;
373  }
374 
375  VertexGeometryId photon;
376  VertexGeometry geom;
377  float pdf;
378  Edf edf;
379 
380  // for now we never perturb the position on the light source, so we just fetch the old one
381  // TODO: if we ever decided to perturb the position we'd also have to track the geometric
382  // acceptance ratio T_ratio = s >= 1 ? (pdf_old / pdf_new) : 0
383  photon = context.vertices[chain_id];
384 
385  // recover the pdf and EDF information
386  context.mesh_light.map(photon.prim_id, photon.uv, &geom, &pdf, &edf);
387 
388  // store the compact vertex information
389  Path path(s + t, context.mut_vertices + chain_id, context.n_chains);
390  path.v_L(0) = photon;
391 
392  // track the bsdf's at each vertex
393  PathCache<float4> f_path(s + t, context.mut_f_vertices + chain_id, context.n_chains);
394 
395  const uint32 packed_normal = pack_direction(geom.normal_s);
396 
397  // update the acceptance ratios
398  context.Q_old[chain_id] *= 1.0f / pdf;
399  context.Q_new[chain_id] *= 1.0f / pdf;
400 
401  // store the photon if and only if s == 1
402  if (s == 1)
403  {
404  context.light_vertices.vertex[chain_id] = VPL(photon.prim_id, photon.uv, 1.0f); // track the acceptance rate factors in the .w component
405  context.light_vertices.vertex_path_id[chain_id] = chain_id;
406  context.light_vertices.vertex_gbuffer[chain_id] = make_uint4(cugar::to_rgbe(edf.color), 0, 0, __float_as_uint(pdf));
407  context.light_vertices.vertex_pos[chain_id] = cugar::Vector4f(geom.position, __uint_as_float(packed_normal));
408  context.light_vertices.vertex_input[chain_id] = make_uint2(0, cugar::to_rgbe(cugar::Vector3f(1.0f)/* / pdf*/)); // the material emission factor
409  context.light_vertices.vertex_weights[chain_id] = PathWeights(
410  0.0f, // p(-2)g(-2)p(-1)
411  1.0f * pdf); // p(-1)g(-1) = pdf : we want p(-1)g(-1)p(0) = p(0)*pdf - which will happen because we are setting p(0) = p(0) and g(-1) = pdf
412  return true; // the light subpath stops here
413  }
414  else
415  {
416  // temporarily generate an invalid photon - used in case the light subpath gets terminated early
417  context.light_vertices.vertex_path_id[chain_id] = uint32(-1);
418  }
419 
420  if (s > 1)
421  {
422  // setup the old path
423  const Path old_path(s + t, context.vertices + chain_id, context.n_chains);
424  const PathCache<float4> old_f_path(s + t, context.f_vertices + chain_id, context.n_chains);
425 
426  const VertexGeometryId old_v = old_path.v_L(0);
427  const VertexGeometryId old_v_next = old_path.v_L(1);
428 
429  // compute the old outgoing direction
430  const cugar::Vector3f out_old = cugar::normalize(
431  interpolate_position(renderer.mesh, old_v_next) -
432  interpolate_position(renderer.mesh, old_v) );
433 
434  // fetch the randoms we need
435  cugar::Vector2f samples;
436  for (uint32 i = 0; i < 2; ++i)
437  samples[i] = context.u_m(chain_id, 3 + i);
438 
439  // apply a spherical perturbation
440  const cugar::Vector3f out = context.enable_mutations ? spherical_perturbation(geom, out_old, samples, context.options.perturbation_radius) : out_old;
441 
442  // evaluate the EDF
443  const cugar::Vector3f f = edf.f( geom, geom.position, out );
444 
445  // cache the first vertex bsdf
446  f_path.v_L(0) = cugar::Vector4f(f, 0.0f);
447 
448  if (cugar::max_comp(f))
449  {
450  FERMAT_ASSERT( cugar::max_comp(f) > 0.0f );
451  Ray out_ray;
452  out_ray.origin = geom.position;
453  out_ray.dir = out;
454  out_ray.tmin = 1.0e-4f;
455  out_ray.tmax = 1.0e8f;
456 
457  context.Q_old[chain_id] *= old_f_path.v_L(0) * cos_theta( interpolate_normal(renderer.mesh, old_v), out_old );
458  context.Q_new[chain_id] *= f_path.v_L(0) * cos_theta( geom.normal_s, out );
459  FERMAT_ASSERT(valid_sample(context.Q_old[chain_id].xyz()));
460 
461  // we use the fourth component to track parts of the acceptance rate
462  const cugar::Vector4f out_w = cugar::Vector4f(f, 0.0f);
463 
464  const float p_proj = edf.p(geom, geom.position, out, cugar::kProjectedSolidAngle);
465  const TempPathWeights out_path_weights = TempPathWeights::light_vertex_1( pdf, p_proj, fabsf(dot(geom.normal_s, out)) );
466 
467  scatter_queue.warp_append(
468  PixelInfo(chain_id, FBufferDesc::DIFFUSE_C),
469  out_ray,
470  out_w,
471  0u, // we need to track the bounce number
472  out_path_weights); // only needed for MIS
473  }
474  else
475  {
476  // kill the new sample
477  context.discard( chain_id );
478  }
479  }
480  return false;
481  }
482 
483  FERMAT_DEVICE
484  bool perturb_secondary_light_vertex(MLTContext& context, RenderingContextView& renderer, RayQueue& in_queue, RayQueue& scatter_queue, const uint32 task_id, uint32* out_chain_id = NULL)
485  {
486  const PixelInfo pixel_info = in_queue.pixels[task_id];
487  const Ray ray = in_queue.rays[task_id];
488  const Hit hit = in_queue.hits[task_id];
489  const cugar::Vector4f w = in_queue.weights[task_id];
490  const TempPathWeights path_weights = in_queue.path_weights[task_id]; // only needed for MIS
491  const uint32 in_bounce = context.in_bounce;
492 
493  const uint32 chain_id = pixel_info.pixel;
494  if (out_chain_id)
495  *out_chain_id = chain_id;
496 
497  const uint32 s = context.st[chain_id].z;
498  const uint32 t = context.st[chain_id].w;
499 
500  if (hit.t > 0.0f && hit.triId >= 0)
501  {
502  // store the compact vertex information
503  Path path(s + t, context.mut_vertices + chain_id, context.n_chains);
504  path.v_L(in_bounce + 1) = VertexGeometryId(hit.triId, hit.u, hit.v);
505 
506  // track the bsdf's at each vertex
507  PathCache<float4> f_path(s + t, context.mut_f_vertices + chain_id, context.n_chains);
508 
509  // setup the light vertex
510  LightVertex lv;
511  lv.setup(ray, hit, w.xyz(), path_weights, in_bounce + 1, renderer);
512 
513  // store the photon / VPL
514  if (in_bounce + 2 == s)
515  {
516  const uint32 slot = chain_id;
517 
518  const uint32 packed_normal = pack_direction(lv.geom.normal_s);
519  const uint32 packed_direction = pack_direction(lv.in);
520 
521  context.light_vertices.vertex[slot] = VPL(hit.triId, cugar::Vector2f(hit.u, hit.v), w.w); // track the acceptance rate factors in the .w component
522  context.light_vertices.vertex_path_id[slot] = pixel_info.pixel;
523  context.light_vertices.vertex_gbuffer[slot] = make_uint4(
524  cugar::to_rgbe(cugar::Vector4f(lv.material.diffuse).xyz()),
525  cugar::to_rgbe(cugar::Vector4f(lv.material.specular).xyz()),
526  cugar::binary_cast<uint32>(lv.material.roughness),
527  cugar::to_rgbe(cugar::Vector4f(lv.material.diffuse_trans).xyz()));
528  context.light_vertices.vertex_pos[slot] = cugar::Vector4f(lv.geom.position, __uint_as_float(packed_normal));
529  context.light_vertices.vertex_input[slot] = make_uint2(packed_direction, cugar::to_rgbe(w.xyz()));
530  context.light_vertices.vertex_weights[slot] = PathWeights(
531  lv.pGp_sum, // f(i-2)g(i-2)f(i-1)
532  lv.prev_pG); // f(i-1)g(i-1)
533  }
534 
535  // trace a bounce ray
536  if (in_bounce + 2 < s)
537  {
538  // fetch the randoms we need
539  cugar::Vector3f samples;
540  for (uint32 i = 0; i < 3; ++i)
541  samples[i] = context.u_m(chain_id, (in_bounce + 2) * 3 + i); // remember we need 6 coordinates just to sample the second vertex (3 for the light position + 3 for the direction)
542 
543  // setup the old path
544  const Path old_path(s + t, context.vertices + chain_id, context.n_chains);
545  const PathCache<float4> old_f_path(s + t, context.f_vertices + chain_id, context.n_chains);
546 
547  const VertexGeometryId old_v = old_path.v_L(in_bounce + 1);
548  const VertexGeometryId old_v_next = old_path.v_L(in_bounce + 2);
549 
550  // compute the outgoing direction in the old path
551  const cugar::Vector3f out_old = cugar::normalize(
552  interpolate_position(renderer.mesh, old_v_next) -
553  interpolate_position(renderer.mesh, old_v));
554 
555  cugar::Vector3f out = out_old;
556 
557  const float u_p = context.enable_mutations ?
558  context.u_m(chain_id, (in_bounce + 2) * 3 + 2) :
559  2.0f; // a value higher than 1
560 
561  if (u_p < context.options.exp_perturbations)
562  {
563  // apply a spherical perturbation
564  out = spherical_perturbation(lv.geom, out_old, samples.xy(), context.options.perturbation_radius);
565  }
566  else if (u_p < context.options.H_perturbations
567  + context.options.exp_perturbations)
568  {
569  // reconstruct the old vertex
570  const LightVertex old_lv = reconstruct_vertex<LightVertex>( old_path.v_L(in_bounce), old_v, in_bounce, renderer );
571 
572  // apply a H-perturbation
573  out = H_perturbation(old_lv, out_old, lv, samples.xy(), context.options.perturbation_radius);
574  }
575  // evaluate the bsdf
576  cugar::Vector3f f = lv.bsdf.f(lv.geom, lv.in, out);
577 
578  // cache this vertex' bsdf
579  f_path.v_L(in_bounce + 1) = cugar::Vector4f(f, 0.0f);
580 
581  // update the acceptance rate factors
582  if (u_p < context.options.exp_perturbations)
583  {
584  context.Q_old[chain_id] *= old_f_path.v_L(in_bounce + 1) * cos_theta( interpolate_normal(renderer.mesh, old_v), out_old );
585  context.Q_new[chain_id] *= f_path.v_L(in_bounce + 1) * cos_theta( lv.geom.normal_s, out );
586  FERMAT_ASSERT(valid_sample(context.Q_old[chain_id].xyz()));
587  }
588  else if (u_p < context.options.H_perturbations
589  + context.options.exp_perturbations)
590  {
591  // compute the geometric portion of the acceptance ratio
592  const LightVertex old_lv = reconstruct_vertex<LightVertex>( old_path.v_L(in_bounce), old_v, in_bounce, renderer );
593 
594  context.Q_old[chain_id] *= old_f_path.v_L(in_bounce + 1) * H_perturbation_geometric_density( old_lv, out_old ) * cos_theta( old_lv.geom.normal_s, out_old );
595  context.Q_new[chain_id] *= f_path.v_L(in_bounce + 1) * H_perturbation_geometric_density( lv, out ) * cos_theta( lv.geom.normal_s, out );
596  FERMAT_ASSERT(valid_sample(context.Q_old[chain_id].xyz()));
597  }
598  else
599  {
600  context.Q_old[chain_id] *= old_f_path.v_L(in_bounce + 1) * cos_theta( interpolate_normal(renderer.mesh, old_v), out_old );
601  context.Q_new[chain_id] *= f_path.v_L(in_bounce + 1) * cos_theta( lv.geom.normal_s, out );
602  FERMAT_ASSERT(valid_sample(context.Q_old[chain_id].xyz()));
603  }
604 
605  // we use the fourth component to track parts of the acceptance rate
606  const cugar::Vector4f out_w = cugar::Vector4f(f * w.xyz(), w.w);
607 
608  if (valid_non_zero_sample( out_w.xyz() ))
609  {
610  // enqueue the output ray
611  Ray out_ray;
612  out_ray.origin = lv.geom.position;
613  out_ray.dir = out;
614  out_ray.tmin = 1.0e-4f;
615  out_ray.tmax = 1.0e8f;
616 
617  const PixelInfo out_pixel = pixel_info;
618 
619  const float p_proj = lv.bsdf.p(lv.geom, lv.in, out, cugar::kProjectedSolidAngle);
620  const TempPathWeights out_path_weights( lv, out, p_proj );
621 
622  scatter_queue.warp_append(
623  out_pixel,
624  out_ray,
625  out_w,
626  in_bounce + 1,
627  out_path_weights ); // only needed for MIS
628  }
629  else
630  {
631  // kill the new sample
632  context.discard( chain_id );
633  }
634  }
635  }
636  else
637  {
638  // hit the environment - nothing to do
639  //
640 
641  // kill the new sample
642  context.discard( chain_id );
643  return true; // the light subpath stops here
644  }
645 
646  return (in_bounce + 2 == s);
647  }
648 
649  FERMAT_DEVICE
650  void perturb_primary_eye_vertex(MLTContext& context, RenderingContextView& renderer, RayQueue& out_queue, const uint32 chain_id, const uint32 out_slot)
651  {
652  const uint32 s = context.st[chain_id].z;
653  const uint32 t = context.st[chain_id].w;
654 
655  // setup the old path
656  Path old_path(s + t, context.vertices + chain_id, context.n_chains);
657 
658  #if 0
659  // fetch the old primary ray direction
660  cugar::Vector3f out_old = cugar::normalize(
661  interpolate_position(renderer.mesh, old_path.v_E(1)) -
662  cugar::Vector3f(renderer.camera.eye));
663 
664  // invert the screen sampling
665  cugar::Vector2f uv = renderer.camera_sampler.invert(out_old);
666  #else
667  // fetch the conveniently pre-packaged screen uv coordinates stored in the zero-th vertex
668  cugar::Vector2f uv = old_path.v_E(0).uv;
669  #endif
670 
671  cugar::Vector3f old_ray_direction = renderer.camera_sampler.sample_direction(uv);
672 
673  const float u_p = context.u_m(chain_id, (s + t - 2)*3 + 2);
674 
675  if (context.enable_mutations && u_p < context.options.screen_perturbations)
676  {
677  // fetch the randoms we need
678  cugar::Vector2f samples;
679  for (uint32 i = 0; i < 2; ++i)
680  samples[i] = context.u_m(chain_id, (s + t - 2)*3 + i);
681 
682  #if SPHERICAL_SCREEN_PERTURBATIONS
683  // and remap it to a new direction
684  const cugar::Vector3f out = exponential_spherical_perturbation(cugar::Vector3f(old_ray_direction), samples, context.options.perturbation_radius);
685 
686  // map the new direction back to screen-space uv's
687  uv = renderer.camera_sampler.invert(out);
688  #elif DISK_SCREEN_SPACE_PERTURBATION
689  // map the samples to a point on the disk
690  const cugar::Bounded_exponential dist(0.0001f, context.options.perturbation_radius);
691  //const cugar::Cauchy_distribution dist(context.options.perturbation_radius);
692 
693  // compute polar coordinates
694  const float phi = samples.x*2.0f*M_PIf;
695  const float r = dist.map(samples.y);
696 
697  const cugar::Vector2f d = cugar::Vector2f( cosf(phi) * r, sinf(phi) * r );
698 
699  // map them to a point and sum all up
700  uv.x = cugar::mod( uv.x + d.x, 1.0f );
701  uv.y = cugar::mod( uv.y + d.y, 1.0f );
702  #endif
703  }
704 
705  // store uv's in vertex 0 (even if one day these coordinates should be dedicated to lens uv's...)
706  Path path(s + t, context.mut_vertices + chain_id, context.n_chains);
707  path.v_E(0) = VertexGeometryId(0, uv);
708 
709  cugar::Vector3f ray_origin = renderer.camera.eye;
710  cugar::Vector3f ray_direction = renderer.camera_sampler.sample_direction(uv);
711 
712  ((float4*)out_queue.rays)[2 * out_slot + 0] = make_float4(ray_origin.x, ray_origin.y, ray_origin.z, 0.0f); // origin, tmin
713  ((float4*)out_queue.rays)[2 * out_slot + 1] = make_float4(ray_direction.x, ray_direction.y, ray_direction.z, 1e34f); // dir, tmax
714 
715  // write the pixel index
716  out_queue.pixels[out_slot] = chain_id;
717 
718  // compute the acceptance ratio
719  const cugar::Vector3f old_out = cugar::normalize( old_ray_direction );
720  const cugar::Vector3f new_out = cugar::normalize( ray_direction );
721 
722  // compute the camera response
723  const float W_e = renderer.camera_sampler.W_e(new_out);
724 
725  // cache the first vertex
726  PathCache<float4> f_path(s + t, context.mut_f_vertices + chain_id, context.n_chains);
727  f_path.v_E(0) = cugar::Vector4f(W_e, W_e, W_e, 0.0f);
728 
729  // fetch the old vertex bsdfs
730  PathCache<float4> old_f_path(s + t, context.f_vertices + chain_id, context.n_chains);
731 
732  #if SPHERICAL_SCREEN_PERTURBATIONS
733  const float old_cos_theta_o = (dot( old_out, renderer.camera_sampler.W ) / renderer.camera_sampler.W_len);
734  const float new_cos_theta_o = (dot( new_out, renderer.camera_sampler.W ) / renderer.camera_sampler.W_len);
735 
736  context.Q_old[chain_id] *= old_f_path.v_E(0) * old_cos_theta_o;
737  context.Q_new[chain_id] *= f_path.v_E(0) * new_cos_theta_o;
738  #else
739  // no real need to track the acceptance rates as they are unity
740  const float new_pdf = renderer.camera_sampler.pdf(new_out, true);
741  const float old_pdf = renderer.camera_sampler.pdf(old_out, true);
742 
743  context.Q_old[chain_id] *= old_pdf ? old_f_path.v_E(0) / old_pdf : cugar::Vector4f(1.0f);
744  context.Q_new[chain_id] *= new_pdf ? f_path.v_E(0) / new_pdf : cugar::Vector4f(0.0f);
745  #endif
746 
747  // write the filter weight
748  out_queue.weights[out_slot] = cugar::Vector4f(W_e, W_e, W_e, 1.0f);
749 
750  // only needed for MIS
751  const float p_e = W_e; // FIXME: only true for the pinhole camera model!
752  const float cos_theta = (dot( new_out, renderer.camera_sampler.W ) / renderer.camera_sampler.W_len);
753  out_queue.path_weights[out_slot] = TempPathWeights::eye_vertex_1( p_e, cos_theta, context.options.light_tracing );
754  }
755 
756  FERMAT_DEVICE
757  void perturb_secondary_eye_vertex(MLTContext& context, RenderingContextView& renderer, RayQueue& in_queue, RayQueue& scatter_queue, const uint32 task_id)
758  {
759  const PixelInfo pixel_info = in_queue.pixels[task_id];
760  const Ray ray = in_queue.rays[task_id];
761  const Hit hit = in_queue.hits[task_id];
762  const cugar::Vector4f w = in_queue.weights[task_id];
763  const TempPathWeights path_weights = in_queue.path_weights[task_id]; // only needed for MIS
764  const uint32 in_bounce = context.in_bounce;
765 
766  const uint32 chain_id = pixel_info.pixel;
767 
768  const uint32 s = context.st[chain_id].z;
769  const uint32 t = context.st[chain_id].w;
770 
771  bool invalidate_sample = false;
772  bool accumulated_path = false;
773  cugar::Vector4f accumulated_value = 0.0f;
774 
775  if (hit.t > 0.0f && hit.triId >= 0)
776  {
777  // store the compact vertex information
778  Path path(s + t, context.mut_vertices + chain_id, context.n_chains);
779  path.v_E(in_bounce + 1) = VertexGeometryId(hit.triId, hit.u, hit.v);
780 
781  // track the bsdf's at each vertex
782  PathCache<float4> f_path(s + t, context.mut_f_vertices + chain_id, context.n_chains);
783 
784  // setup the old path
785  const Path old_path(s + t, context.vertices + chain_id, context.n_chains);
786  PathCache<float4> old_f_path(s + t, context.f_vertices + chain_id, context.n_chains);
787 
788  // setup the eye vertex
789  EyeVertex ev;
790  ev.setup(ray, hit, w.xyz(), path_weights, in_bounce, renderer);
791 
792  // perform a single bidirectional connection if and only if we are at vertex 't and 's > 0
793  if (in_bounce + 2 == t && s > 0) // NOTE: in_bounce + 2 represents the technique, i.e. the number of eye subpath vertices
794  {
795  // fetch the light vertex stored for this chain
796  const uint32 light_idx = chain_id;
797 
798  const uint32 light_depth = s - 1; // NOTE: s represents the technique, i.e. the number of light subpath vertices. The last vertex has index s - 1.
799 
800  // setup a light vertex
801  cugar::Vector4f light_pos = context.light_vertices.vertex_pos[light_idx];
802  const uint2 light_in = context.light_vertices.vertex_input[light_idx];
803  uint4 light_gbuffer = context.light_vertices.vertex_gbuffer[light_idx];
804  PathWeights light_weights = context.light_vertices.vertex_weights[light_idx];
805  const uint32 light_path_id = context.light_vertices.vertex_path_id[light_idx];
806 
807  if (light_path_id != uint32(-1))
808  {
809  // setup the light vertex
810  LightVertex lv(light_pos, light_in, light_gbuffer, light_weights, light_depth, renderer);
811 
812  // evaluate the connection
813  cugar::Vector3f out;
814  cugar::Vector3f out_f;
815  cugar::Vector3f out_f_s;
816  cugar::Vector3f out_f_L;
817  float out_G;
818  float d;
819  float mis_w;
820 
821  eval_connection_terms(ev, lv, out, out_f_s, out_f_L, out_G, d, mis_w, false);
822 
823  //if (cugar::length( lv.edf.color - cugar::Vector3f(3.0f, 5.0f, 10.0f)) > 1.0f)
824  // printf("%f, %f, %f\n", lv.edf.color.x, lv.edf.color.y, lv.edf.color.z);
825 
826  if (!context.options.mis)
827  mis_w = 1.0f;
828 
829  out_f = out_f_L * out_f_s;
830 
831  // cache the connecting bsdf's
832  f_path.v_E(t-1) = cugar::Vector4f(out_f_s, 0.0f);
833  f_path.v_L(s-1) = cugar::Vector4f(out_f_L, 0.0f);
834 
835  // compute the G factor of the old path
836  const float old_G = old_path.G(s-1, renderer);
837 
838  // compute the acceptance rate factors we need
839  context.Q_old[chain_id] *= old_f_path.v_E(t-1) * old_f_path.v_L(s-1) * old_G;
840  context.Q_new[chain_id] *= f_path.v_E(t-1) * f_path.v_L(s-1) * out_G;
841  context.discard_invalid( chain_id );
842 
843  cugar::Vector4f out_w = cugar::Vector4f( ev.alpha * lv.alpha * out_f * mis_w, 0.0f );
844 
845  if (valid_non_zero_sample( out_w.xyz() ))
846  {
847  // enqueue the output ray
848  Ray out_ray;
849  out_ray.origin = ev.geom.position + ev.in * SHADOW_BIAS; // move the origin slightly towards the viewer
850  out_ray.dir = (lv.geom.position - out_ray.origin); //out;
851  out_ray.tmin = SHADOW_TMIN;
852  out_ray.tmax = 0.9999f;
853 
854  const PixelInfo out_pixel = in_bounce ?
855  pixel_info : // if this sample is a secondary bounce, use the previously selected channel
856  PixelInfo(pixel_info.pixel, FBufferDesc::DIRECT_C); // otherwise (i.e. this is the first bounce) choose the direct-lighting output channel
857 
858  context.shadow_queue.warp_append(
859  out_pixel,
860  out_ray,
861  out_w,
862  in_bounce );
863 
864  accumulated_path = true;
865  }
866  else
867  {
868  // kill the new sample
869  invalidate_sample = true;
870  }
871  }
872  else
873  {
874  // kill the new sample
875  invalidate_sample = true;
876  }
877  }
878 
879  // accumulate the emissive component along the incoming direction
880  if (in_bounce + 2 == t && s == 0) // NOTE: in_bounce + 2 represents the technique, i.e. the number of eye subpath vertices
881  {
882  // evaluate the edf's output along the incoming direction
883  VertexGeometry light_vertex_geom;
884  float light_pdf;
885  Edf light_edf;
886 
887  // recover the pdf and EDF information
888  context.mesh_light.map(ev.geom_id.prim_id, ev.geom_id.uv, &light_vertex_geom, &light_pdf, &light_edf);
889 
890  const cugar::Vector3f f_L = light_edf.f(light_vertex_geom, light_vertex_geom.position, ev.in);
891 
892  // cache the bsdf corresponding to the light vertex
893  f_path.v_L(0) = cugar::Vector4f(f_L, 0.0f);
894 
895  // compute the acceptance rate factors we need
896  context.Q_old[chain_id] *= old_f_path.v_L(0);
897  context.Q_new[chain_id] *= f_path.v_L(0);
898  context.discard_invalid( chain_id );
899 
900  float mis_w = 1.0f;
901  if (context.options.mis)
902  {
903  // compute the MIS weight
904  const float p_L = light_edf.p(light_vertex_geom, light_vertex_geom.position, ev.in, cugar::kProjectedSolidAngle);
905  const float pGp = p_L * light_pdf;
906  const float prev_pGp = ev.prev_pG * p_L;
907  mis_w = mis_selector(
908  0, ev.depth + 2,
909  (ev.depth == 0 || pGp == 0.0f) ? 1.0f :
910  bpt_mis(pGp, prev_pGp, ev.pGp_sum));
911  }
912 
913  const cugar::Vector4f out_w = cugar::Vector4f(w.xyz() * f_L * mis_w, 0.0f);
914 
915  if (valid_non_zero_sample( out_w.xyz() ))
916  accumulated_value = out_w;
917  else
918  {
919  // kill the new sample
920  invalidate_sample = true;
921  }
922 
923  accumulated_path = false;
924  }
925 
926  // trace a bounce ray
927  if (in_bounce + 2 < t) // NOTE: in_bounce + 2 represents the technique, i.e. the number of eye subpath vertices
928  {
929  // fetch the randoms we need
930  cugar::Vector3f samples;
931  for (uint32 i = 0; i < 3; ++i)
932  samples[i] = context.u_m(chain_id, (s + t - in_bounce - 2) * 3 + i); // remember we need 6 coordinates just to sample the second vertex
933 
934  const VertexGeometryId old_v = old_path.v_E(in_bounce + 1);
935  const VertexGeometryId old_v_next = old_path.v_E(in_bounce + 2);
936 
937  // compute the outgoing direction in the old path
938  const cugar::Vector3f out_old = cugar::normalize(
939  interpolate_position(renderer.mesh, old_v_next) -
940  interpolate_position(renderer.mesh, old_v));
941 
942  cugar::Vector3f out = out_old;
943 
944  const float u_p = context.enable_mutations ?
945  context.u_m(chain_id, (s + t - in_bounce - 2) * 3 + 2) :
946  2.0f; // a value higher than 1
947 
948  if (u_p < context.options.exp_perturbations)
949  {
950  // apply a spherical perturbation
951  out = spherical_perturbation(ev.geom, out_old, samples.xy(), context.options.perturbation_radius);
952  }
953  else if (u_p < context.options.H_perturbations
954  + context.options.exp_perturbations)
955  {
956  // reconstruct the old vertex
957  const EyeVertex old_ev = reconstruct_vertex<EyeVertex>( old_path.v_E(in_bounce), old_v, in_bounce, renderer );
958 
959  // apply a H-perturbation
960  out = H_perturbation(old_ev, out_old, ev, samples.xy(), context.options.perturbation_radius);
961  }
962 
963  // evaluate the bsdf
964  cugar::Vector3f f = ev.bsdf.f(ev.geom, ev.in, out);
965 
966  // cache this vertex' bsdf
967  f_path.v_E(in_bounce + 1) = cugar::Vector4f(f, 0.0f);
968 
969  // update the acceptance rate factors
970  if (u_p < context.options.exp_perturbations)
971  {
972  context.Q_old[chain_id] *= old_f_path.v_E(in_bounce + 1) * cos_theta( interpolate_normal(renderer.mesh, old_v), out_old );
973  context.Q_new[chain_id] *= f_path.v_E(in_bounce + 1) * cos_theta( ev.geom.normal_s, out );
974  context.discard_invalid(chain_id);
975  }
976  else if (u_p < context.options.H_perturbations
977  + context.options.exp_perturbations)
978  {
979  // compute the geometric portion of the acceptance ratio
980  const EyeVertex old_ev = reconstruct_vertex<EyeVertex>( old_path.v_E(in_bounce), old_v, in_bounce, renderer );
981 
982  context.Q_old[chain_id] *= old_f_path.v_E(in_bounce + 1) * H_perturbation_geometric_density( old_ev, out_old ) * cos_theta( old_ev.geom.normal_s, out_old );
983  context.Q_new[chain_id] *= f_path.v_E(in_bounce + 1) * H_perturbation_geometric_density( ev, out ) * cos_theta( ev.geom.normal_s, out );
984  context.discard_invalid(chain_id);
985  }
986  else
987  {
988  context.Q_old[chain_id] *= old_f_path.v_E(in_bounce + 1) * cos_theta( interpolate_normal(renderer.mesh, old_v), out_old );
989  context.Q_new[chain_id] *= f_path.v_E(in_bounce + 1) * cos_theta( ev.geom.normal_s, out );
990  context.discard_invalid( chain_id );
991  }
992 
993  // we use the fourth component to track parts of the acceptance rate
994  const cugar::Vector4f out_w = cugar::Vector4f(f * w.xyz(), w.w);
995 
996  if (valid_non_zero_sample( out_w.xyz() ))
997  {
998  const uint32 channel = FBufferDesc::COMPOSITED_C;
999 
1000  // enqueue the output ray
1001  Ray out_ray;
1002  out_ray.origin = ev.geom.position;
1003  out_ray.dir = out;
1004  out_ray.tmin = 1.0e-4f;
1005  out_ray.tmax = 1.0e8f;
1006 
1007  const PixelInfo out_pixel = in_bounce ?
1008  pixel_info : // if this sample is a secondary bounce, use the previously selected channel
1009  PixelInfo(pixel_info.pixel, channel); // otherwise (i.e. this is the first bounce) choose the output channel for the rest of the path
1010 
1011  const float p_proj = ev.bsdf.p(ev.geom, ev.in, out, cugar::kProjectedSolidAngle);
1012  const TempPathWeights out_path_weights( ev, out, p_proj );
1013 
1014  scatter_queue.warp_append(
1015  out_pixel, out_ray,
1016  out_w,
1017  in_bounce + 1,
1018  out_path_weights ); // only needed for MIS
1019 
1020  accumulated_path = true;
1021  }
1022  else
1023  {
1024  // kill the new sample
1025  invalidate_sample = true;
1026  }
1027  }
1028  }
1029  else
1030  {
1031  // hit the environment - perform sky lighting
1032  //
1033 
1034  // kill the new sample
1035  invalidate_sample = true;
1036  }
1037 
1038  if (invalidate_sample)
1039  {
1040  // kill the new sample
1041  context.discard( chain_id );
1042  }
1043 
1044  if (accumulated_path == false)
1045  {
1046  // perform the MH acceptance-rejection test
1047  accept_reject_accumulate(chain_id, s, t, accumulated_value, context, renderer);
1048  }
1049  }
1050 
1051  FERMAT_DEVICE
1052  void solve_occlusion_mlt(MLTContext& context, RenderingContextView& renderer, const uint32 task_id)
1053  {
1054  const PixelInfo pixel_info = context.shadow_queue.pixels[task_id];
1055  const Hit hit = context.shadow_queue.hits[task_id];
1056  const cugar::Vector4f w = context.shadow_queue.weights[task_id];
1057 
1058  const uint32 chain_id = pixel_info.pixel;
1059  const uint32 s = context.st[chain_id].z;
1060  const uint32 t = context.st[chain_id].w;
1061 
1062  const float vis = (hit.t < 0.0f) ? 1.0f : 0.0f;
1063 
1064  // update the acceptance rate with visibility
1065  context.Q_new[chain_id] *= vis;
1066 
1067  // perform the MH acceptance-rejection test
1068  accept_reject_accumulate(chain_id, s, t, w * vis, context, renderer);
1069  }
1070 
1071  //------------------------------------------------------------------------------
1072 
1073 } // anonymous namespace
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE float pdf(const cugar::Vector3f out, const bool projected=false) const
Definition: camera.h:286
CUGAR_HOST_DEVICE float map(const float U) const
Definition: distributions.h:255
CUGAR_HOST_DEVICE uint32 quantize(const float x, const uint32 n)
Definition: numbers.h:600
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE float W_e(const cugar::Vector3f out) const
Definition: camera.h:294
Defines various distributions.
float CUGAR_HOST_DEVICE mod(const float x, const float m)
Definition: numbers.h:606
Definition: bpt_utils.h:110
Definition: vertex.h:105
Definition: lights.h:299
Definition: distributions.h:234
Definition: ray_queues.h:57
Definition: lights.h:59
FERMAT_HOST_DEVICE void map(const uint32_t prim_id, const cugar::Vector2f &uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:584
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float atomic_add(float *value, const float op)
Definition: atomics.h:100
Definition: vertex.h:92
Definition: bpt_utils.h:131
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE cugar::Vector3f sample_direction(const cugar::Vector2f ndc) const
Definition: camera.h:278
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE T dot(const Vector< T, DIM > &op1, const Vector< T, DIM > &op2)
Definition: tensor_inl.h:309
Definition: tiled_sequence.h:53
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE uint32 pack_direction(const cugar::Vector3f &dir)
Definition: vertex.h:123
Definition: bpt_utils.h:583
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f(const DifferentialGeometry &geometry, const Vector3f in, const Vector3f out) const
Definition: lambert_edf.h:60
Definition: ray.h:42
Definition: ray.h:68
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f in, const Vector3f out, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert_edf.h:80
Definition: bpt_context.h:54
Definition: bpt_kernels.h:216
FERMAT_HOST_DEVICE void eval_connection_terms(const EyeVertex ev, const LightVertex &lv, cugar::Vector3f &out, cugar::Vector3f &f_conn, float &G, float &d)
Definition: bpt_utils.h:768
Definition: path.h:55
Definition: bpt_kernels.h:56
Definition: edf.h:49
Definition: mlt_core.h:98
Definition: pathtracer_core.h:527
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 cugar::Vector2f invert(const cugar::Vector3f out) const
Definition: camera.h:305
Definition: bpt_utils.h:311
FERMAT_HOST_DEVICE cugar::Vector3f interpolate_normal(const MeshView &mesh, const uint32 tri_id, const float u, const float v)
Definition: mesh_utils.h:350
Define CUDA based warp adders.
Definition: path.h:236
Definition: mlt.h:133
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float randfloat(unsigned i, unsigned p)
Definition: numbers.h:753
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE float p(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, const cugar::SphericalMeasure measure=cugar::kProjectedSolidAngle, const bool RR=true, const ComponentType components=kAllComponents) const
Definition: bsdf.h:474