Fermat
lights.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 #include <types.h>
32 #include <ray.h>
33 #include <vertex.h>
34 #include <mesh_utils.h>
35 #include <texture.h>
36 #include <edf.h>
37 #include <vtl.h>
38 #include <cugar/basic/algorithms.h>
40 
43 
46 
47 enum class LightType
48 {
49  kPoint = 0,
50  kDisk = 1,
51  kRectangle = 2,
52  kDirectional = 3,
53  kMesh = 4,
54  kVTL = 5,
55 };
56 
60 {
61  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
62  VPL() {}
63 
64  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
65  VPL(const uint32 _prim_id, const float2 _uv, const float _e)
66  {
67  VertexGeometryId::prim_id = _prim_id;
68  VertexGeometryId::uv = _uv;
69  E = _e;
70  }
71 
72  float E;
73 
74  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
75  static float pdf(const float4 E) { return cugar::max3(fabsf(E.x), fabsf(E.y), fabsf(E.z)); }
76 };
77 
86 struct Light
87 {
88  LightType type;
89 
90 #if !defined(OPTIX_COMPILATION)
91  FERMAT_HOST_DEVICE
92  Light() : type(LightType::kPoint) {}
93 #else
94  FERMAT_HOST_DEVICE
95  Light() {}
96 #endif
97 
98  FERMAT_HOST_DEVICE
99  Light(LightType _type) : type(_type) {}
100 
111  FERMAT_HOST_DEVICE
112  bool sample(
113  const float* Z,
114  uint32_t* prim_id,
115  cugar::Vector2f* uv,
116  VertexGeometry* geom,
117  float* pdf,
118  Edf* edf) const;
119 
131  FERMAT_HOST_DEVICE
132  bool sample(
133  const cugar::Vector3f p,
134  const float* Z,
135  uint32_t* prim_id,
136  cugar::Vector2f* uv,
137  VertexGeometry* geom,
138  float* pdf,
139  Edf* edf) const;
140 
147  FERMAT_HOST_DEVICE
148  void intersect(const Ray ray, float2* uv, float* t) const;
149 
158  FERMAT_HOST_DEVICE
159  void map(const uint32_t prim_id, const cugar::Vector2f& uv, VertexGeometry* geom, float* pdf, Edf* edf) const;
160 
169  FERMAT_HOST_DEVICE
170  void map(const uint32_t prim_id, const cugar::Vector2f& uv, const VertexGeometry& geom, float* pdf, Edf* edf) const;
171 };
172 
175 struct DiskLight : public Light
176 {
177  cugar::Vector3f pos;
178  cugar::Vector3f dir;
179  cugar::Vector3f u;
180  cugar::Vector3f v;
181  cugar::Vector3f color;
182  float radius;
183 
184 #if !defined(OPTIX_COMPILATION)
185  FERMAT_HOST_DEVICE
186  DiskLight() : Light(LightType::kDisk) {}
187 #endif
188  FERMAT_HOST_DEVICE
192  const float* Z,
193  uint32_t* prim_id,
194  cugar::Vector2f* uv,
195  VertexGeometry* geom,
196  float* pdf,
197  Edf* edf) const
198  {
199  *prim_id = 0;
200 
201  *uv = cugar::Vector2f(Z[0], Z[1]);
202 
203  map_impl(*prim_id, *uv, geom, pdf, edf);
204  return false;
205  }
206 
209  FERMAT_HOST_DEVICE
210  void intersect_impl(const Ray ray, float2* uv, float* t) const
211  {
212  // TODO
213  *t = -1;
214  }
215 
218  FERMAT_HOST_DEVICE
219  void map_impl(const uint32_t prim_id, const cugar::Vector2f& uv, VertexGeometry* geom, float* pdf, Edf* edf) const
220  {
221  const float2 disk = cugar::square_to_unit_disk( uv );
222 
223  geom->position = pos +
224  u * disk.x * radius +
225  v * disk.y * radius;
226 
227  geom->normal_g = geom->normal_s = cugar::normalize(dir);
228  geom->tangent = cugar::orthogonal(geom->normal_g);
229  geom->binormal = cugar::cross(geom->normal_g, geom->tangent);
230 
231  *pdf = 1.0f / (M_PIf * radius*radius);
232 
233  // TODO: write the EDF
234  }
235 
238  FERMAT_HOST_DEVICE
239  void map_impl(const uint32_t prim_id, const cugar::Vector2f& uv, const VertexGeometry& geom, float* pdf, Edf* edf) const
240  {
241  *pdf = 1.0f / (M_PIf * radius*radius);
242 
243  // TODO: write the EDF
244  }
245 };
246 
249 struct DirectionalLight : public Light
250 {
251  cugar::Vector3f dir;
252  cugar::Vector3f color;
253 
254 #if !defined(OPTIX_COMPILATION)
255  FERMAT_HOST_DEVICE
256  DirectionalLight() : Light(LightType::kDirectional) {}
257 #endif
258  FERMAT_HOST_DEVICE
262  const float* Z,
263  uint32_t* prim_id,
264  cugar::Vector2f* uv,
265  VertexGeometry* geom,
266  float* pdf,
267  Edf* edf) const
268  {
269  // sample a point on the scene's projected bounding disk in direction 'dir
270  return true;
271  }
272 
275  FERMAT_HOST_DEVICE
277  const cugar::Vector3f p,
278  const float* Z,
279  uint32_t* prim_id,
280  cugar::Vector2f* uv,
281  VertexGeometry* geom,
282  float* pdf,
283  Edf* edf) const
284  {
285  const float FAR = 1.0e8f;
286 
287  geom->position = p - dir * FAR;
288  geom->normal_s = geom->normal_g = dir;
289  geom->tangent = cugar::orthogonal(dir);
290  geom->binormal = cugar::cross(dir,geom->tangent);
291  *pdf = 1.0f;
292  *edf = Edf( FAR*FAR * color );
293  return true;
294  }
295 };
296 
299 struct MeshLight : public Light
300 {
301 #if !defined(OPTIX_COMPILATION)
302  FERMAT_HOST_DEVICE
303  MeshLight() : Light(LightType::kMesh) {}
304 #else
305  FERMAT_HOST_DEVICE
306  MeshLight() {}
307 #endif
308 
309  FERMAT_HOST_DEVICE
310  MeshLight(const uint32 _n_prims, const float* _prims_cdf, const float* _prims_inv_area, MeshView _mesh, const MipMapView* _textures, const uint32 _n_vpls, const float* _vpls_cdf, const VPL* _vpls, const float _norm) :
311  Light(LightType::kMesh), n_prims(_n_prims), prims_cdf(_prims_cdf), prims_inv_area(_prims_inv_area), mesh(_mesh), textures(_textures), n_vpls(_n_vpls), vpls_cdf(_vpls_cdf), vpls(_vpls), norm(_norm) {}
312 
315  FERMAT_HOST_DEVICE
317  const float* Z,
318  uint32_t* prim_id,
319  cugar::Vector2f* uv,
320  VertexGeometry* geom,
321  float* pdf,
322  Edf* edf) const
323  {
324  const float one = cugar::binary_cast<float>(FERMAT_ALMOST_ONE_AS_INT);
325 
326  if (n_vpls)
327  {
328  // sample one of the VPLs
329  const uint32 l = cugar::min( uint32(Z[2] * float(n_vpls)), n_vpls-1 );
330  *prim_id = vpls[l].prim_id;
331  *uv = vpls[l].uv;
332 
333  map_impl(*prim_id, *uv, geom, pdf, edf);
334  }
335  else if (n_prims)
336  {
337  // sample one of the primitives
338  const uint32 tri_id = cugar::upper_bound_index( cugar::min( Z[2], one ), prims_cdf, n_prims);
339  FERMAT_ASSERT(tri_id < n_prims);
340 
341  *prim_id = tri_id;
342  *uv = cugar::Vector2f(Z[0],Z[1]);
343 
344  // make sure uv is in the triangle range
345  if (uv->x + uv->y > 1.0f)
346  {
347  uv->x = 1.0f - uv->x;
348  uv->y = 1.0f - uv->y;
349  }
350 
351  map_impl(*prim_id, *uv, geom, pdf, edf);
352  }
353  else
354  {
355  *prim_id = 0;
356  *uv = cugar::Vector2f(0.0f);
357  *pdf = 1.0f;
358  *edf = Edf();
359  }
360  return false;
361  }
362 
365  FERMAT_HOST_DEVICE
366  void intersect_impl(const Ray ray, float2* uv, float* t) const
367  {
368  // TODO
369  }
370 
373  FERMAT_HOST_DEVICE
374  void map_impl(const uint32_t prim_id, const cugar::Vector2f& uv, VertexGeometry* geom, float* pdf, Edf* edf) const
375  {
376  if (n_vpls || n_prims)
377  {
378  FERMAT_ASSERT(prim_id < uint32(mesh.num_triangles));
379  setup_differential_geometry(mesh, prim_id, uv.x, uv.y, geom);
380 
381  const int material_id = mesh.material_indices[prim_id];
382  MeshMaterial material = mesh.materials[material_id];
383 
384  material.emissive = cugar::Vector4f(material.emissive) * texture_lookup(geom->texture_coords, material.emissive_map, textures, cugar::Vector4f(1.0f));
385 
386  if (n_vpls)
387  *pdf = VPL::pdf(material.emissive) / norm;
388  else
389  *pdf = (prims_cdf[prim_id] - (prim_id ? prims_cdf[prim_id-1] : 0)) * prims_inv_area[prim_id];
390 
391  *edf = Edf(material);
392  }
393  else
394  {
395  geom->position = cugar::Vector3f(0.0f, 0.0f, 0.0f);
396  geom->tangent = cugar::Vector3f(1.0f, 0.0f, 0.0f);
397  geom->binormal = cugar::Vector3f(0.0f, 1.0f, 0.0f);
398  geom->normal_s = cugar::Vector3f(0.0f, 0.0f, 1.0f);
399  geom->normal_g = cugar::Vector3f(0.0f, 0.0f, 1.0f);
400 
401  *pdf = 1.0f;
402  *edf = Edf();
403  }
404  }
405 
408  FERMAT_HOST_DEVICE
409  void map_impl(const uint32_t prim_id, const cugar::Vector2f& uv, const VertexGeometry& geom, float* pdf, Edf* edf) const
410  {
411  if (n_vpls || n_prims)
412  {
413  FERMAT_ASSERT(prim_id < uint32(mesh.num_triangles));
414  const int material_id = mesh.material_indices[prim_id];
415  MeshMaterial material = mesh.materials[material_id];
416 
417  material.emissive = cugar::Vector4f(material.emissive) * texture_lookup(geom.texture_coords, material.emissive_map, textures, cugar::Vector4f(1.0f));
418 
419  if (n_vpls)
420  *pdf = VPL::pdf(material.emissive) / norm;
421  else
422  *pdf = (prims_cdf[prim_id] - (prim_id ? prims_cdf[prim_id - 1] : 0)) * prims_inv_area[prim_id];
423 
424  *edf = Edf(material);
425  }
426  else
427  {
428  *pdf = 1.0f;
429  *edf = Edf();
430  }
431  }
432 
435  FERMAT_HOST_DEVICE
436  bool invert_impl(const uint32_t prim_id, const cugar::Vector2f& uv, const float* in_Z, float* out_Z, float* out_pdf) const
437  {
438  if (n_prims)
439  {
440  FERMAT_ASSERT(prim_id < uint32(mesh.num_triangles));
441  const float cdf_1 = prim_id ? prims_cdf[prim_id - 1] : 0;
442  const float cdf_2 = prims_cdf[prim_id];
443  const float cdf_delta = cdf_2 - cdf_1;
444 
445  out_Z[0] = uv.x;
446  out_Z[1] = uv.y;
447  out_Z[2] = in_Z[0] * cdf_delta + cdf_1;
448 
449  *out_pdf = prim_area(mesh, prim_id) / cdf_delta;
450  //*out_pdf = 1.0f / (cdf_delta * prim_area( mesh, prim_id ));
451 
452  return cdf_delta > 0.0f; // if cdf_delta == 0 the inversion process actually works, but the pdf becomes a Dirac delta
453  }
454  else
455  {
456  out_Z[0] = out_Z[1] = out_Z[2] = 0.5f;
457  *out_pdf = 1.0f;
458  return true;
459  }
460  }
461 
464  FERMAT_HOST_DEVICE
465  float inverse_pdf_impl(const uint32_t prim_id, const cugar::Vector2f& uv, const float* out_Z) const
466  {
467  if (n_prims)
468  {
469  FERMAT_ASSERT(prim_id < uint32(mesh.num_triangles));
470  const float cdf_1 = prim_id ? prims_cdf[prim_id - 1] : 0;
471  const float cdf_2 = prims_cdf[prim_id];
472  const float cdf_delta = cdf_2 - cdf_1;
473 
474  // if cdf_delta == 0 the inversion process actually works, but the pdf becomes a Dirac delta
475  return prim_area(mesh, prim_id) / cdf_delta;
476  //return 1.0f / (cdf_delta * prim_area(mesh, prim_id));
477  }
478  else
479  return 1.0f;
480  }
481 
482  FERMAT_HOST_DEVICE
483  uint32 vpl_count() const { return n_vpls; }
484 
485  FERMAT_HOST_DEVICE
486  VPL get_vpl(const uint32 i) const { return vpls[i]; }
487 
490  FERMAT_HOST_DEVICE
491  void map_vpl(
492  const uint32 vpl_idx,
493  uint32_t* prim_id,
494  cugar::Vector2f* uv,
495  VertexGeometry* geom,
496  float* pdf,
497  Edf* edf) const
498  {
499  // sample one of the VPLs
500  *prim_id = vpls[vpl_idx].prim_id;
501  *uv = vpls[vpl_idx].uv;
502 
503  map_impl(*prim_id, *uv, geom, pdf, edf);
504  }
505 
506  uint32 n_prims;
507  const float* prims_cdf;
508  const float* prims_inv_area;
509  MeshView mesh;
510  const MipMapView* textures;
511  uint32 n_vpls;
512  const float* vpls_cdf;
513  const VPL* vpls;
514  float norm;
515 };
516 
517 
518 // sample a point on the light source
519 //
520 FERMAT_HOST_DEVICE
521 inline bool Light::sample(
522  const float* Z,
523  uint32_t* prim_id,
524  cugar::Vector2f* uv,
525  VertexGeometry* geom,
526  float* pdf,
527  Edf* edf) const
528 {
529  switch (type)
530  {
531  case LightType::kDisk:
532  return reinterpret_cast<const DiskLight*>(this)->sample_impl( Z, prim_id, uv, geom, pdf, edf );
533  case LightType::kMesh:
534  return reinterpret_cast<const MeshLight*>(this)->sample_impl( Z, prim_id, uv, geom, pdf, edf );
535  case LightType::kDirectional:
536  return reinterpret_cast<const DirectionalLight*>(this)->sample_impl( Z, prim_id, uv, geom, pdf, edf );
537  }
538  return true;
539 }
540 
541 // sample a point on the light source
542 //
543 FERMAT_HOST_DEVICE
544 inline bool Light::sample(
545  const cugar::Vector3f p,
546  const float* Z,
547  uint32_t* prim_id,
548  cugar::Vector2f* uv,
549  VertexGeometry* geom,
550  float* pdf,
551  Edf* edf) const
552 {
553  switch (type)
554  {
555  case LightType::kDisk:
556  return reinterpret_cast<const DiskLight*>(this)->sample_impl( Z, prim_id, uv, geom, pdf, edf ); // NOTE: for now using the generic, non-anchored implementation
557  case LightType::kMesh:
558  return reinterpret_cast<const MeshLight*>(this)->sample_impl( Z, prim_id, uv, geom, pdf, edf ); // NOTE: for now using the generic, non-anchored implementation
559  case LightType::kDirectional:
560  return reinterpret_cast<const DirectionalLight*>(this)->sample_impl( p, Z, prim_id, uv, geom, pdf, edf );
561  }
562  return true;
563 }
564 
565 // intersect the given ray with the light source
566 //
567 FERMAT_HOST_DEVICE
568 inline void Light::intersect(const Ray ray, float2* uv, float* t) const
569 {
570  switch (type)
571  {
572  case LightType::kDisk:
573  reinterpret_cast<const DiskLight*>(this)->intersect_impl( ray, uv, t );
574  break;
575  case LightType::kMesh:
576  reinterpret_cast<const MeshLight*>(this)->intersect_impl(ray, uv, t);
577  break;
578  }
579 }
580 
581 // map a (prim,uv) pair to a surface element
582 //
583 FERMAT_HOST_DEVICE
584 inline void Light::map(const uint32_t prim_id, const cugar::Vector2f& uv, VertexGeometry* geom, float* pdf, Edf* edf) const
585 {
586  switch (type)
587  {
588  case LightType::kDisk:
589  reinterpret_cast<const DiskLight*>(this)->map_impl( prim_id, uv, geom, pdf, edf );
590  break;
591  case LightType::kMesh:
592  reinterpret_cast<const MeshLight*>(this)->map_impl( prim_id, uv, geom, pdf, edf );
593  break;
594  }
595 }
596 
597 // map a (prim,uv) pair to a surface element
598 //
599 FERMAT_HOST_DEVICE
600 inline void Light::map(const uint32_t prim_id, const cugar::Vector2f& uv, const VertexGeometry& geom, float* pdf, Edf* edf) const
601 {
602  switch (type)
603  {
604  case LightType::kDisk:
605  reinterpret_cast<const DiskLight*>(this)->map_impl(prim_id, uv, geom, pdf, edf);
606  break;
607  case LightType::kMesh:
608  reinterpret_cast<const MeshLight*>(this)->map_impl(prim_id, uv, geom, pdf, edf);
609  break;
610  }
611 }
612 
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE index_type upper_bound_index(const Value x, Iterator begin, const index_type n)
Definition: algorithms.h:193
FERMAT_HOST_DEVICE void map_impl(const uint32_t prim_id, const cugar::Vector2f &uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:219
FERMAT_HOST_DEVICE bool sample_impl(const float *Z, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:261
Definition: vertex.h:105
Definition: lights.h:299
Definition: lights.h:249
FERMAT_HOST_DEVICE float prim_area(const MeshView &mesh, const uint32 tri_id)
Definition: mesh_utils.h:168
FERMAT_HOST_DEVICE void intersect_impl(const Ray ray, float2 *uv, float *t) const
Definition: lights.h:210
Definition: lights.h:59
FERMAT_HOST_DEVICE bool sample_impl(const float *Z, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:316
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
FERMAT_HOST_DEVICE bool sample_impl(const float *Z, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:191
Defines various spherical mappings.
Definition: MeshView.h:55
Definition: vertex.h:92
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
Defines some general purpose algorithms.
Definition: lights.h:86
FERMAT_HOST_DEVICE void map_impl(const uint32_t prim_id, const cugar::Vector2f &uv, const VertexGeometry &geom, float *pdf, Edf *edf) const
Definition: lights.h:239
Definition: lights.h:175
Definition: MeshView.h:96
Definition: ray.h:42
CUGAR_HOST CUGAR_DEVICE Vector2f square_to_unit_disk(const Vector2f uv)
Definition: mappings_inline.h:56
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 void intersect_impl(const Ray ray, float2 *uv, float *t) const
Definition: lights.h:366
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_HOST_DEVICE void intersect(const Ray ray, float2 *uv, float *t) const
Definition: lights.h:568
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Out binary_cast(const In in)
Definition: types.h:288
FERMAT_HOST_DEVICE bool sample(const float *Z, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:521
Definition: texture_view.h:73
Definition: edf.h:49
FERMAT_HOST_DEVICE void map_impl(const uint32_t prim_id, const cugar::Vector2f &uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:374
FERMAT_HOST_DEVICE void map_vpl(const uint32 vpl_idx, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:491
FERMAT_HOST_DEVICE void map_impl(const uint32_t prim_id, const cugar::Vector2f &uv, const VertexGeometry &geom, float *pdf, Edf *edf) const
Definition: lights.h:409
FERMAT_HOST_DEVICE bool sample_impl(const cugar::Vector3f p, const float *Z, uint32_t *prim_id, cugar::Vector2f *uv, VertexGeometry *geom, float *pdf, Edf *edf) const
Definition: lights.h:276