OpenVDB  8.1.1
RayTracer.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
17 
18 #ifndef OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED
19 #define OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED
20 
21 #include <openvdb/Types.h>
22 #include <openvdb/math/BBox.h>
23 #include <openvdb/math/Ray.h>
24 #include <openvdb/math/Math.h>
27 #include <deque>
28 #include <iostream>
29 #include <fstream>
30 #include <limits>
31 #include <memory>
32 #include <string>
33 #include <type_traits>
34 #include <vector>
35 
36 namespace openvdb {
38 namespace OPENVDB_VERSION_NAME {
39 namespace tools {
40 
41 // Forward declarations
42 class BaseCamera;
43 class BaseShader;
44 
46 template<typename GridT>
47 inline void rayTrace(const GridT&,
48  const BaseShader&,
49  BaseCamera&,
50  size_t pixelSamples = 1,
51  unsigned int seed = 0,
52  bool threaded = true);
53 
55 template<typename GridT, typename IntersectorT>
56 inline void rayTrace(const GridT&,
57  const IntersectorT&,
58  const BaseShader&,
59  BaseCamera&,
60  size_t pixelSamples = 1,
61  unsigned int seed = 0,
62  bool threaded = true);
63 
64 
66 
69 template<typename GridT, typename IntersectorT = tools::LevelSetRayIntersector<GridT> >
71 {
72 public:
73  using GridType = GridT;
74  using Vec3Type = typename IntersectorT::Vec3Type;
75  using RayType = typename IntersectorT::RayType;
76 
78  LevelSetRayTracer(const GridT& grid,
79  const BaseShader& shader,
80  BaseCamera& camera,
81  size_t pixelSamples = 1,
82  unsigned int seed = 0);
83 
86  LevelSetRayTracer(const IntersectorT& inter,
87  const BaseShader& shader,
88  BaseCamera& camera,
89  size_t pixelSamples = 1,
90  unsigned int seed = 0);
91 
94 
97 
99  void setGrid(const GridT& grid);
100 
103  void setIntersector(const IntersectorT& inter);
104 
112  void setShader(const BaseShader& shader);
113 
115  void setCamera(BaseCamera& camera);
116 
121  void setPixelSamples(size_t pixelSamples, unsigned int seed = 0);
122 
124  void render(bool threaded = true) const;
125 
128  void operator()(const tbb::blocked_range<size_t>& range) const;
129 
130 private:
131  const bool mIsMaster;
132  double* mRand;
133  IntersectorT mInter;
134  std::unique_ptr<const BaseShader> mShader;
135  BaseCamera* mCamera;
136  size_t mSubPixels;
137 };// LevelSetRayTracer
138 
139 
141 
146 template <typename IntersectorT, typename SamplerT = tools::BoxSampler>
148 {
149 public:
150 
151  using GridType = typename IntersectorT::GridType;
152  using RayType = typename IntersectorT::RayType;
153  using ValueType = typename GridType::ValueType;
154  using AccessorType = typename GridType::ConstAccessor;
156  static_assert(std::is_floating_point<ValueType>::value,
157  "VolumeRender requires a floating-point-valued grid");
158 
160  VolumeRender(const IntersectorT& inter, BaseCamera& camera);
161 
163  VolumeRender(const VolumeRender& other);
164 
166  void render(bool threaded=true) const;
167 
169  void setCamera(BaseCamera& camera) { mCamera = &camera; }
170 
173  void setIntersector(const IntersectorT& inter);
174 
177  void setLightDir(Real x, Real y, Real z) { mLightDir = Vec3R(x,y,z).unit(); }
178 
180  void setLightColor(Real r, Real g, Real b) { mLightColor = Vec3R(r,g,b); }
181 
183  void setPrimaryStep(Real primaryStep) { mPrimaryStep = primaryStep; }
184 
186  void setShadowStep(Real shadowStep) { mShadowStep = shadowStep; }
187 
189  void setScattering(Real x, Real y, Real z) { mScattering = Vec3R(x,y,z); }
190 
192  void setAbsorption(Real x, Real y, Real z) { mAbsorption = Vec3R(x,y,z); }
193 
196  void setLightGain(Real gain) { mLightGain = gain; }
197 
199  void setCutOff(Real cutOff) { mCutOff = cutOff; }
200 
205  void print(std::ostream& os = std::cout, int verboseLevel = 1);
206 
209  void operator()(const tbb::blocked_range<size_t>& range) const;
210 
211 private:
212 
213  AccessorType mAccessor;
214  BaseCamera* mCamera;
215  std::unique_ptr<IntersectorT> mPrimary, mShadow;
216  Real mPrimaryStep, mShadowStep, mCutOff, mLightGain;
217  Vec3R mLightDir, mLightColor, mAbsorption, mScattering;
218 };//VolumeRender
219 
221 
224 class Film
225 {
226 public:
229  struct RGBA
230  {
231  using ValueT = float;
232 
233  RGBA() : r(0), g(0), b(0), a(1) {}
234  explicit RGBA(ValueT intensity) : r(intensity), g(intensity), b(intensity), a(1) {}
235  RGBA(ValueT _r, ValueT _g, ValueT _b, ValueT _a = static_cast<ValueT>(1.0)):
236  r(_r), g(_g), b(_b), a(_a)
237  {}
238  RGBA(double _r, double _g, double _b, double _a = 1.0)
239  : r(static_cast<ValueT>(_r))
240  , g(static_cast<ValueT>(_g))
241  , b(static_cast<ValueT>(_b))
242  , a(static_cast<ValueT>(_a))
243  {}
244 
245  RGBA operator* (ValueT scale) const { return RGBA(r*scale, g*scale, b*scale);}
246  RGBA operator+ (const RGBA& rhs) const { return RGBA(r+rhs.r, g+rhs.g, b+rhs.b);}
247  RGBA operator* (const RGBA& rhs) const { return RGBA(r*rhs.r, g*rhs.g, b*rhs.b);}
248  RGBA& operator+=(const RGBA& rhs) { r+=rhs.r; g+=rhs.g; b+=rhs.b; a+=rhs.a; return *this;}
249 
250  void over(const RGBA& rhs)
251  {
252  const float s = rhs.a*(1.0f-a);
253  r = a*r+s*rhs.r;
254  g = a*g+s*rhs.g;
255  b = a*b+s*rhs.b;
256  a = a + s;
257  }
258 
259  ValueT r, g, b, a;
260  };
261 
262 
263  Film(size_t width, size_t height)
264  : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize])
265  {
266  }
267  Film(size_t width, size_t height, const RGBA& bg)
268  : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize])
269  {
270  this->fill(bg);
271  }
272 
273  const RGBA& pixel(size_t w, size_t h) const
274  {
275  assert(w < mWidth);
276  assert(h < mHeight);
277  return mPixels[w + h*mWidth];
278  }
279 
280  RGBA& pixel(size_t w, size_t h)
281  {
282  assert(w < mWidth);
283  assert(h < mHeight);
284  return mPixels[w + h*mWidth];
285  }
286 
287  void fill(const RGBA& rgb=RGBA(0)) { for (size_t i=0; i<mSize; ++i) mPixels[i] = rgb; }
288  void checkerboard(const RGBA& c1=RGBA(0.3f), const RGBA& c2=RGBA(0.6f), size_t size=32)
289  {
290  RGBA *p = mPixels.get();
291  for (size_t j = 0; j < mHeight; ++j) {
292  for (size_t i = 0; i < mWidth; ++i, ++p) {
293  *p = ((i & size) ^ (j & size)) ? c1 : c2;
294  }
295  }
296  }
297 
298  void savePPM(const std::string& fileName)
299  {
300  std::string name(fileName);
301  if (name.find_last_of(".") == std::string::npos) name.append(".ppm");
302 
303  std::unique_ptr<unsigned char[]> buffer(new unsigned char[3*mSize]);
304  unsigned char *tmp = buffer.get(), *q = tmp;
305  RGBA* p = mPixels.get();
306  size_t n = mSize;
307  while (n--) {
308  *q++ = static_cast<unsigned char>(255.0f*(*p ).r);
309  *q++ = static_cast<unsigned char>(255.0f*(*p ).g);
310  *q++ = static_cast<unsigned char>(255.0f*(*p++).b);
311  }
312 
313  std::ofstream os(name.c_str(), std::ios_base::binary);
314  if (!os.is_open()) {
315  std::cerr << "Error opening PPM file \"" << name << "\"" << std::endl;
316  return;
317  }
318 
319  os << "P6\n" << mWidth << " " << mHeight << "\n255\n";
320  os.write(reinterpret_cast<const char*>(&(*tmp)), 3 * mSize * sizeof(unsigned char));
321  }
322 
323  size_t width() const { return mWidth; }
324  size_t height() const { return mHeight; }
325  size_t numPixels() const { return mSize; }
326  const RGBA* pixels() const { return mPixels.get(); }
327 
328 private:
329  size_t mWidth, mHeight, mSize;
330  std::unique_ptr<RGBA[]> mPixels;
331 };// Film
332 
333 
335 
338 {
339 public:
340  BaseCamera(Film& film, const Vec3R& rotation, const Vec3R& translation,
341  double frameWidth, double nearPlane, double farPlane)
342  : mFilm(&film)
343  , mScaleWidth(frameWidth)
344  , mScaleHeight(frameWidth * double(film.height()) / double(film.width()))
345  {
346  assert(nearPlane > 0 && farPlane > nearPlane);
347  mScreenToWorld.accumPostRotation(math::X_AXIS, rotation[0] * M_PI / 180.0);
348  mScreenToWorld.accumPostRotation(math::Y_AXIS, rotation[1] * M_PI / 180.0);
349  mScreenToWorld.accumPostRotation(math::Z_AXIS, rotation[2] * M_PI / 180.0);
350  mScreenToWorld.accumPostTranslation(translation);
351  this->initRay(nearPlane, farPlane);
352  }
353 
354  virtual ~BaseCamera() {}
355 
356  Film::RGBA& pixel(size_t i, size_t j) { return mFilm->pixel(i, j); }
357 
358  size_t width() const { return mFilm->width(); }
359  size_t height() const { return mFilm->height(); }
360 
365  void lookAt(const Vec3R& xyz, const Vec3R& up = Vec3R(0.0, 1.0, 0.0))
366  {
367  const Vec3R orig = mScreenToWorld.applyMap(Vec3R(0.0));
368  const Vec3R dir = orig - xyz;
369  try {
370  Mat4d xform = math::aim<Mat4d>(dir, up);
371  xform.postTranslate(orig);
372  mScreenToWorld = math::AffineMap(xform);
373  this->initRay(mRay.t0(), mRay.t1());
374  } catch (...) {}
375  }
376 
377  Vec3R rasterToScreen(double i, double j, double z) const
378  {
379  return Vec3R( (2 * i / double(mFilm->width()) - 1) * mScaleWidth,
380  (1 - 2 * j / double(mFilm->height())) * mScaleHeight, z );
381  }
382 
386  virtual math::Ray<double> getRay(
387  size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const = 0;
388 
389 protected:
390  void initRay(double t0, double t1)
391  {
392  mRay.setTimes(t0, t1);
393  mRay.setEye(mScreenToWorld.applyMap(Vec3R(0.0)));
394  mRay.setDir(mScreenToWorld.applyJacobian(Vec3R(0.0, 0.0, -1.0)));
395  }
396 
398  double mScaleWidth, mScaleHeight;
401 };// BaseCamera
402 
403 
405 {
406  public:
423  const Vec3R& rotation = Vec3R(0.0),
424  const Vec3R& translation = Vec3R(0.0),
425  double focalLength = 50.0,
426  double aperture = 41.2136,
427  double nearPlane = 1e-3,
428  double farPlane = std::numeric_limits<double>::max())
429  : BaseCamera(film, rotation, translation, 0.5*aperture/focalLength, nearPlane, farPlane)
430  {
431  }
432 
433  ~PerspectiveCamera() override = default;
434 
439  size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const override
440  {
441  math::Ray<double> ray(mRay);
442  Vec3R dir = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, -1.0);
443  dir = BaseCamera::mScreenToWorld.applyJacobian(dir);
444  dir.normalize();
445  ray.scaleTimes(1.0/dir.dot(ray.dir()));
446  ray.setDir(dir);
447  return ray;
448  }
449 
452  static double focalLengthToFieldOfView(double length, double aperture)
453  {
454  return 360.0 / M_PI * atan(aperture/(2.0*length));
455  }
458  static double fieldOfViewToFocalLength(double fov, double aperture)
459  {
460  return aperture/(2.0*(tan(fov * M_PI / 360.0)));
461  }
462 };// PerspectiveCamera
463 
464 
466 {
467 public:
481  const Vec3R& rotation = Vec3R(0.0),
482  const Vec3R& translation = Vec3R(0.0),
483  double frameWidth = 1.0,
484  double nearPlane = 1e-3,
485  double farPlane = std::numeric_limits<double>::max())
486  : BaseCamera(film, rotation, translation, 0.5*frameWidth, nearPlane, farPlane)
487  {
488  }
489  ~OrthographicCamera() override = default;
490 
492  size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const override
493  {
494  math::Ray<double> ray(mRay);
495  Vec3R eye = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, 0.0);
496  ray.setEye(BaseCamera::mScreenToWorld.applyMap(eye));
497  return ray;
498  }
499 };// OrthographicCamera
500 
501 
503 
504 
507 {
508 public:
511  BaseShader(const BaseShader&) = default;
512  virtual ~BaseShader() = default;
517  virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& nml, const Vec3R& dir) const = 0;
518  virtual BaseShader* copy() const = 0;
519 };
520 
521 
528 template<typename GridT = Film::RGBA,
529  typename SamplerType = tools::PointSampler>
530 class MatteShader: public BaseShader
531 {
532 public:
533  MatteShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {}
534  MatteShader(const MatteShader&) = default;
535  ~MatteShader() override = default;
536  Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override
537  {
538  typename GridT::ValueType v = zeroVal<typename GridT::ValueType>();
539  SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v);
540  return Film::RGBA(v[0], v[1], v[2]);
541  }
542  BaseShader* copy() const override { return new MatteShader<GridT, SamplerType>(*this); }
543 
544 private:
545  typename GridT::ConstAccessor mAcc;
546  const math::Transform* mXform;
547 };
548 
549 // Template specialization using a constant color of the material.
550 template<typename SamplerType>
551 class MatteShader<Film::RGBA, SamplerType>: public BaseShader
552 {
553 public:
554  MatteShader(const Film::RGBA& c = Film::RGBA(1.0f)): mRGBA(c) {}
555  MatteShader(const MatteShader&) = default;
556  ~MatteShader() override = default;
557  Film::RGBA operator()(const Vec3R&, const Vec3R&, const Vec3R&) const override
558  {
559  return mRGBA;
560  }
561  BaseShader* copy() const override { return new MatteShader<Film::RGBA, SamplerType>(*this); }
562 
563 private:
564  const Film::RGBA mRGBA;
565 };
566 
567 
575 template<typename GridT = Film::RGBA,
576  typename SamplerType = tools::PointSampler>
578 {
579 public:
580  NormalShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {}
581  NormalShader(const NormalShader&) = default;
582  ~NormalShader() override = default;
583  Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R&) const override
584  {
585  typename GridT::ValueType v = zeroVal<typename GridT::ValueType>();
586  SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v);
587  return Film::RGBA(v[0]*(normal[0]+1.0), v[1]*(normal[1]+1.0), v[2]*(normal[2]+1.0));
588  }
589  BaseShader* copy() const override { return new NormalShader<GridT, SamplerType>(*this); }
590 
591 private:
592  typename GridT::ConstAccessor mAcc;
593  const math::Transform* mXform;
594 };
595 
596 // Template specialization using a constant color of the material.
597 template<typename SamplerType>
598 class NormalShader<Film::RGBA, SamplerType>: public BaseShader
599 {
600 public:
601  NormalShader(const Film::RGBA& c = Film::RGBA(1.0f)) : mRGBA(c*0.5f) {}
602  NormalShader(const NormalShader&) = default;
603  ~NormalShader() override = default;
604  Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R&) const override
605  {
606  return mRGBA * Film::RGBA(normal[0] + 1.0, normal[1] + 1.0, normal[2] + 1.0);
607  }
608  BaseShader* copy() const override { return new NormalShader<Film::RGBA, SamplerType>(*this); }
609 
610 private:
611  const Film::RGBA mRGBA;
612 };
613 
614 
622 template<typename GridT = Film::RGBA,
623  typename SamplerType = tools::PointSampler>
625 {
626 public:
627  PositionShader(const math::BBox<Vec3R>& bbox, const GridT& grid)
628  : mMin(bbox.min())
629  , mInvDim(1.0/bbox.extents())
630  , mAcc(grid.getAccessor())
631  , mXform(&grid.transform())
632  {
633  }
634  PositionShader(const PositionShader&) = default;
635  ~PositionShader() override = default;
636  Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override
637  {
638  typename GridT::ValueType v = zeroVal<typename GridT::ValueType>();
639  SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v);
640  const Vec3R rgb = (xyz - mMin) * mInvDim;
641  return Film::RGBA(v[0],v[1],v[2]) * Film::RGBA(rgb[0], rgb[1], rgb[2]);
642  }
643  BaseShader* copy() const override { return new PositionShader<GridT, SamplerType>(*this); }
644 
645 private:
646  const Vec3R mMin, mInvDim;
647  typename GridT::ConstAccessor mAcc;
648  const math::Transform* mXform;
649 };
650 
651 // Template specialization using a constant color of the material.
652 template<typename SamplerType>
653 class PositionShader<Film::RGBA, SamplerType>: public BaseShader
654 {
655 public:
656  PositionShader(const math::BBox<Vec3R>& bbox, const Film::RGBA& c = Film::RGBA(1.0f))
657  : mMin(bbox.min()), mInvDim(1.0/bbox.extents()), mRGBA(c) {}
658  PositionShader(const PositionShader&) = default;
659  ~PositionShader() override = default;
660  Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const override
661  {
662  const Vec3R rgb = (xyz - mMin)*mInvDim;
663  return mRGBA*Film::RGBA(rgb[0], rgb[1], rgb[2]);
664  }
665  BaseShader* copy() const override { return new PositionShader<Film::RGBA, SamplerType>(*this); }
666 
667 private:
668  const Vec3R mMin, mInvDim;
669  const Film::RGBA mRGBA;
670 };
671 
672 
682 template<typename GridT = Film::RGBA,
683  typename SamplerType = tools::PointSampler>
685 {
686 public:
687  DiffuseShader(const GridT& grid): mAcc(grid.getAccessor()), mXform(&grid.transform()) {}
688  DiffuseShader(const DiffuseShader&) = default;
689  ~DiffuseShader() override = default;
690  Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R& rayDir) const override
691  {
692  typename GridT::ValueType v = zeroVal<typename GridT::ValueType>();
693  SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v);
694  // We take the abs of the dot product corresponding to having
695  // light sources at +/- rayDir, i.e., two-sided shading.
696  return Film::RGBA(v[0],v[1],v[2])
697  * static_cast<Film::RGBA::ValueT>(math::Abs(normal.dot(rayDir)));
698  }
699  BaseShader* copy() const override { return new DiffuseShader<GridT, SamplerType>(*this); }
700 
701 private:
702  typename GridT::ConstAccessor mAcc;
703  const math::Transform* mXform;
704 };
705 
706 // Template specialization using a constant color of the material.
707 template <typename SamplerType>
708 class DiffuseShader<Film::RGBA, SamplerType>: public BaseShader
709 {
710 public:
711  DiffuseShader(const Film::RGBA& d = Film::RGBA(1.0f)): mRGBA(d) {}
712  DiffuseShader(const DiffuseShader&) = default;
713  ~DiffuseShader() override = default;
714  Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R& rayDir) const override
715  {
716  // We assume a single directional light source at the camera,
717  // so the cosine of the angle between the surface normal and the
718  // direction of the light source becomes the dot product of the
719  // surface normal and inverse direction of the ray. We also ignore
720  // negative dot products, corresponding to strict one-sided shading.
721  //return mRGBA * math::Max(0.0, normal.dot(-rayDir));
722 
723  // We take the abs of the dot product corresponding to having
724  // light sources at +/- rayDir, i.e., two-sided shading.
725  return mRGBA * static_cast<Film::RGBA::ValueT>(math::Abs(normal.dot(rayDir)));
726  }
727  BaseShader* copy() const override { return new DiffuseShader<Film::RGBA, SamplerType>(*this); }
728 
729 private:
730  const Film::RGBA mRGBA;
731 };
732 
733 
735 
736 template<typename GridT>
737 inline void rayTrace(const GridT& grid,
738  const BaseShader& shader,
739  BaseCamera& camera,
740  size_t pixelSamples,
741  unsigned int seed,
742  bool threaded)
743 {
745  tracer(grid, shader, camera, pixelSamples, seed);
746  tracer.render(threaded);
747 }
748 
749 
750 template<typename GridT, typename IntersectorT>
751 inline void rayTrace(const GridT&,
752  const IntersectorT& inter,
753  const BaseShader& shader,
754  BaseCamera& camera,
755  size_t pixelSamples,
756  unsigned int seed,
757  bool threaded)
758 {
759  LevelSetRayTracer<GridT, IntersectorT> tracer(inter, shader, camera, pixelSamples, seed);
760  tracer.render(threaded);
761 }
762 
763 
765 
766 
767 template<typename GridT, typename IntersectorT>
769 LevelSetRayTracer(const GridT& grid,
770  const BaseShader& shader,
771  BaseCamera& camera,
772  size_t pixelSamples,
773  unsigned int seed)
774  : mIsMaster(true),
775  mRand(nullptr),
776  mInter(grid),
777  mShader(shader.copy()),
778  mCamera(&camera)
779 {
780  this->setPixelSamples(pixelSamples, seed);
781 }
782 
783 template<typename GridT, typename IntersectorT>
785 LevelSetRayTracer(const IntersectorT& inter,
786  const BaseShader& shader,
787  BaseCamera& camera,
788  size_t pixelSamples,
789  unsigned int seed)
790  : mIsMaster(true),
791  mRand(nullptr),
792  mInter(inter),
793  mShader(shader.copy()),
794  mCamera(&camera)
795 {
796  this->setPixelSamples(pixelSamples, seed);
797 }
798 
799 template<typename GridT, typename IntersectorT>
802  mIsMaster(false),
803  mRand(other.mRand),
804  mInter(other.mInter),
805  mShader(other.mShader->copy()),
806  mCamera(other.mCamera),
807  mSubPixels(other.mSubPixels)
808 {
809 }
810 
811 template<typename GridT, typename IntersectorT>
814 {
815  if (mIsMaster) delete [] mRand;
816 }
817 
818 template<typename GridT, typename IntersectorT>
820 setGrid(const GridT& grid)
821 {
822  assert(mIsMaster);
823  mInter = IntersectorT(grid);
824 }
825 
826 template<typename GridT, typename IntersectorT>
828 setIntersector(const IntersectorT& inter)
829 {
830  assert(mIsMaster);
831  mInter = inter;
832 }
833 
834 template<typename GridT, typename IntersectorT>
836 setShader(const BaseShader& shader)
837 {
838  assert(mIsMaster);
839  mShader.reset(shader.copy());
840 }
841 
842 template<typename GridT, typename IntersectorT>
845 {
846  assert(mIsMaster);
847  mCamera = &camera;
848 }
849 
850 template<typename GridT, typename IntersectorT>
852 setPixelSamples(size_t pixelSamples, unsigned int seed)
853 {
854  assert(mIsMaster);
855  if (pixelSamples == 0) {
856  OPENVDB_THROW(ValueError, "pixelSamples must be larger than zero!");
857  }
858  mSubPixels = pixelSamples - 1;
859  delete [] mRand;
860  if (mSubPixels > 0) {
861  mRand = new double[16];
862  math::Rand01<double> rand(seed);//offsets for anti-aliaing by jittered super-sampling
863  for (size_t i=0; i<16; ++i) mRand[i] = rand();
864  } else {
865  mRand = nullptr;
866  }
867 }
868 
869 template<typename GridT, typename IntersectorT>
871 render(bool threaded) const
872 {
873  tbb::blocked_range<size_t> range(0, mCamera->height());
874  threaded ? tbb::parallel_for(range, *this) : (*this)(range);
875 }
876 
877 template<typename GridT, typename IntersectorT>
879 operator()(const tbb::blocked_range<size_t>& range) const
880 {
881  const BaseShader& shader = *mShader;
882  Vec3Type xyz, nml;
883  const float frac = 1.0f / (1.0f + float(mSubPixels));
884  for (size_t j=range.begin(), n=0, je = range.end(); j<je; ++j) {
885  for (size_t i=0, ie = mCamera->width(); i<ie; ++i) {
886  Film::RGBA& bg = mCamera->pixel(i,j);
887  RayType ray = mCamera->getRay(i, j);//primary ray
888  Film::RGBA c = mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg;
889  for (size_t k=0; k<mSubPixels; ++k, n +=2 ) {
890  ray = mCamera->getRay(i, j, mRand[n & 15], mRand[(n+1) & 15]);
891  c += mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg;
892  }//loop over sub-pixels
893  bg = c*frac;
894  }//loop over image height
895  }//loop over image width
896 }
897 
899 
900 template<typename IntersectorT, typename SampleT>
902 VolumeRender(const IntersectorT& inter, BaseCamera& camera)
903  : mAccessor(inter.grid().getConstAccessor())
904  , mCamera(&camera)
905  , mPrimary(new IntersectorT(inter))
906  , mShadow(new IntersectorT(inter))
907  , mPrimaryStep(1.0)
908  , mShadowStep(3.0)
909  , mCutOff(0.005)
910  , mLightGain(0.2)
911  , mLightDir(Vec3R(0.3, 0.3, 0).unit())
912  , mLightColor(0.7, 0.7, 0.7)
913  , mAbsorption(0.1)
914  , mScattering(1.5)
915 {
916 }
917 
918 template<typename IntersectorT, typename SampleT>
921  : mAccessor(other.mAccessor)
922  , mCamera(other.mCamera)
923  , mPrimary(new IntersectorT(*(other.mPrimary)))
924  , mShadow(new IntersectorT(*(other.mShadow)))
925  , mPrimaryStep(other.mPrimaryStep)
926  , mShadowStep(other.mShadowStep)
927  , mCutOff(other.mCutOff)
928  , mLightGain(other.mLightGain)
929  , mLightDir(other.mLightDir)
930  , mLightColor(other.mLightColor)
931  , mAbsorption(other.mAbsorption)
932  , mScattering(other.mScattering)
933 {
934 }
935 
936 template<typename IntersectorT, typename SampleT>
938 print(std::ostream& os, int verboseLevel)
939 {
940  if (verboseLevel>0) {
941  os << "\nPrimary step: " << mPrimaryStep
942  << "\nShadow step: " << mShadowStep
943  << "\nCutoff: " << mCutOff
944  << "\nLightGain: " << mLightGain
945  << "\nLightDir: " << mLightDir
946  << "\nLightColor: " << mLightColor
947  << "\nAbsorption: " << mAbsorption
948  << "\nScattering: " << mScattering << std::endl;
949  }
950  mPrimary->print(os, verboseLevel);
951 }
952 
953 template<typename IntersectorT, typename SampleT>
955 setIntersector(const IntersectorT& inter)
956 {
957  mPrimary.reset(new IntersectorT(inter));
958  mShadow.reset( new IntersectorT(inter));
959 }
960 
961 template<typename IntersectorT, typename SampleT>
963 render(bool threaded) const
964 {
965  tbb::blocked_range<size_t> range(0, mCamera->height());
966  threaded ? tbb::parallel_for(range, *this) : (*this)(range);
967 }
968 
969 template<typename IntersectorT, typename SampleT>
971 operator()(const tbb::blocked_range<size_t>& range) const
972 {
973  SamplerType sampler(mAccessor, mShadow->grid().transform());//light-weight wrapper
974 
975  // Any variable prefixed with p (or s) means it's associated with a primary (or shadow) ray
976  const Vec3R extinction = -mScattering-mAbsorption, One(1.0);
977  const Vec3R albedo = mLightColor*mScattering/(mScattering+mAbsorption);//single scattering
978  const Real sGain = mLightGain;//in-scattering along shadow ray
979  const Real pStep = mPrimaryStep;//Integration step along primary ray in voxel units
980  const Real sStep = mShadowStep;//Integration step along shadow ray in voxel units
981  const Real cutoff = mCutOff;//Cutoff for density and transmittance
982 
983  // For the sake of completeness we show how to use two different
984  // methods (hits/march) in VolumeRayIntersector that produce
985  // segments along the ray that intersects active values. Comment out
986  // the line below to use VolumeRayIntersector::march instead of
987  // VolumeRayIntersector::hits.
988 #define USE_HITS
989 #ifdef USE_HITS
990  std::vector<typename RayType::TimeSpan> pTS, sTS;
991  //std::deque<typename RayType::TimeSpan> pTS, sTS;
992 #endif
993 
994  RayType sRay(Vec3R(0), mLightDir);//Shadow ray
995  for (size_t j=range.begin(), je = range.end(); j<je; ++j) {
996  for (size_t i=0, ie = mCamera->width(); i<ie; ++i) {
997  Film::RGBA& bg = mCamera->pixel(i, j);
998  bg.a = bg.r = bg.g = bg.b = 0;
999  RayType pRay = mCamera->getRay(i, j);// Primary ray
1000  if( !mPrimary->setWorldRay(pRay)) continue;
1001  Vec3R pTrans(1.0), pLumi(0.0);
1002 #ifndef USE_HITS
1003  Real pT0, pT1;
1004  while (mPrimary->march(pT0, pT1)) {
1005  for (Real pT = pStep*ceil(pT0/pStep); pT <= pT1; pT += pStep) {
1006 #else
1007  mPrimary->hits(pTS);
1008  for (size_t k=0; k<pTS.size(); ++k) {
1009  Real pT = pStep*ceil(pTS[k].t0/pStep), pT1=pTS[k].t1;
1010  for (; pT <= pT1; pT += pStep) {
1011 #endif
1012  Vec3R pPos = mPrimary->getWorldPos(pT);
1013  const Real density = sampler.wsSample(pPos);
1014  if (density < cutoff) continue;
1015  const Vec3R dT = math::Exp(extinction * density * pStep);
1016  Vec3R sTrans(1.0);
1017  sRay.setEye(pPos);
1018  if( !mShadow->setWorldRay(sRay)) continue;
1019 #ifndef USE_HITS
1020  Real sT0, sT1;
1021  while (mShadow->march(sT0, sT1)) {
1022  for (Real sT = sStep*ceil(sT0/sStep); sT <= sT1; sT+= sStep) {
1023 #else
1024  mShadow->hits(sTS);
1025  for (size_t l=0; l<sTS.size(); ++l) {
1026  Real sT = sStep*ceil(sTS[l].t0/sStep), sT1=sTS[l].t1;
1027  for (; sT <= sT1; sT+= sStep) {
1028 #endif
1029  const Real d = sampler.wsSample(mShadow->getWorldPos(sT));
1030  if (d < cutoff) continue;
1031  sTrans *= math::Exp(extinction * d * sStep/(1.0+sT*sGain));
1032  if (sTrans.lengthSqr()<cutoff) goto Luminance;//Terminate sRay
1033  }//Integration over shadow segment
1034  }// Shadow ray march
1035  Luminance:
1036  pLumi += albedo * sTrans * pTrans * (One-dT);
1037  pTrans *= dT;
1038  if (pTrans.lengthSqr()<cutoff) goto Pixel; // Terminate Ray
1039  }//Integration over primary segment
1040  }// Primary ray march
1041  Pixel:
1042  bg.r = static_cast<Film::RGBA::ValueT>(pLumi[0]);
1043  bg.g = static_cast<Film::RGBA::ValueT>(pLumi[1]);
1044  bg.b = static_cast<Film::RGBA::ValueT>(pLumi[2]);
1045  bg.a = static_cast<Film::RGBA::ValueT>(1.0f - pTrans.sum()/3.0f);
1046  }//Horizontal pixel scan
1047  }//Vertical pixel scan
1048 }
1049 
1050 } // namespace tools
1051 } // namespace OPENVDB_VERSION_NAME
1052 } // namespace openvdb
1053 
1054 #endif // OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED
Definition: RayTracer.h:404
void setDir(const Vec3Type &dir)
Definition: Ray.h:66
Film::RGBA operator()(const Vec3R &xyz, const Vec3R &, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:660
Film::RGBA operator()(const Vec3R &, const Vec3R &normal, const Vec3R &rayDir) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:714
virtual math::Ray< double > getRay(size_t i, size_t j, double iOffset=0.5, double jOffset=0.5) const =0
Return a Ray in world space given the pixel indices and optional offsets in the range [0...
VolumeRender(const IntersectorT &inter, BaseCamera &camera)
Constructor taking an intersector and a base camera.
Definition: RayTracer.h:902
RGBA()
Definition: RayTracer.h:233
PerspectiveCamera(Film &film, const Vec3R &rotation=Vec3R(0.0), const Vec3R &translation=Vec3R(0.0), double focalLength=50.0, double aperture=41.2136, double nearPlane=1e-3, double farPlane=std::numeric_limits< double >::max())
Constructor.
Definition: RayTracer.h:422
void checkerboard(const RGBA &c1=RGBA(0.3f), const RGBA &c2=RGBA(0.6f), size_t size=32)
Definition: RayTracer.h:288
typename IntersectorT::Vec3Type Vec3Type
Definition: RayTracer.h:74
~LevelSetRayTracer()
Destructor.
Definition: RayTracer.h:813
A (very) simple multithreaded ray tracer specifically for narrow-band level sets. ...
Definition: RayTracer.h:70
static double focalLengthToFieldOfView(double length, double aperture)
Return the horizontal field of view in degrees given a focal lenth in mm and the specified aperture i...
Definition: RayTracer.h:452
Color shader that treats position (x, y, z) as an RGB color in a cube defined from an axis-aligned bo...
Definition: RayTracer.h:624
void scaleTimes(RealT scale)
Definition: Ray.h:84
RGBA(ValueT intensity)
Definition: RayTracer.h:234
typename IntersectorT::RayType RayType
Definition: RayTracer.h:152
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:74
typename IntersectorT::GridType GridType
Definition: RayTracer.h:151
MatType unit(const MatType &mat, typename MatType::value_type eps=1.0e-8)
Return a copy of the given matrix with its upper 3×3 rows normalized.
Definition: Mat.h:670
Coord Abs(const Coord &xyz)
Definition: Coord.h:515
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Film::RGBA operator()(const Vec3R &xyz, const Vec3R &, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:536
size_t width() const
Definition: RayTracer.h:358
size_t height() const
Definition: RayTracer.h:359
MatType rotation(const Quat< typename MatType::value_type > &q, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the rotation matrix specified by the given quaternion.
Definition: Mat.h:194
double mScaleWidth
Definition: RayTracer.h:398
Axis-aligned bounding box.
Definition: BBox.h:23
A simple class that allows for concurrent writes to pixels in an image, background initialization of ...
Definition: RayTracer.h:224
size_t height() const
Definition: RayTracer.h:324
void operator()(const tbb::blocked_range< size_t > &range) const
Public method required by tbb::parallel_for.
Definition: RayTracer.h:971
RGBA & pixel(size_t w, size_t h)
Definition: RayTracer.h:280
void setPrimaryStep(Real primaryStep)
Set the integration step-size in voxel units for the primay ray.
Definition: RayTracer.h:183
BaseShader * copy() const override
Definition: RayTracer.h:727
DiffuseShader(const Film::RGBA &d=Film::RGBA(1.0f))
Definition: RayTracer.h:711
MatteShader(const Film::RGBA &c=Film::RGBA(1.0f))
Definition: RayTracer.h:554
Simple generator of random numbers over the range [0, 1)
Definition: Math.h:165
virtual BaseShader * copy() const =0
Definition: RayTracer.h:465
Film(size_t width, size_t height)
Definition: RayTracer.h:263
void setIntersector(const IntersectorT &inter)
Set the intersector that performs the actual intersection of the rays against the volume...
Definition: RayTracer.h:955
Floating-point RGBA components in the range [0, 1].
Definition: RayTracer.h:229
void operator()(const tbb::blocked_range< size_t > &range) const
Public method required by tbb::parallel_for.
Definition: RayTracer.h:879
bool normalize(T eps=T(1.0e-7))
this = normalized this
Definition: Vec3.h:366
OrthographicCamera(Film &film, const Vec3R &rotation=Vec3R(0.0), const Vec3R &translation=Vec3R(0.0), double frameWidth=1.0, double nearPlane=1e-3, double farPlane=std::numeric_limits< double >::max())
Constructor.
Definition: RayTracer.h:480
Definition: Math.h:907
math::Vec3< Real > Vec3R
Definition: openvdb/Types.h:68
typename IntersectorT::RayType RayType
Definition: RayTracer.h:75
void setEye(const Vec3Type &eye)
Definition: Ray.h:64
Film(size_t width, size_t height, const RGBA &bg)
Definition: RayTracer.h:267
const Vec3T & dir() const
Definition: Ray.h:99
void setLightGain(Real gain)
Set parameter that imitates multi-scattering. A value of zero implies no multi-scattering.
Definition: RayTracer.h:196
void setShadowStep(Real shadowStep)
Set the integration step-size in voxel units for the primay ray.
Definition: RayTracer.h:186
void setShader(const BaseShader &shader)
Set the shader derived from the abstract BaseShader class.
Definition: RayTracer.h:836
PositionShader(const math::BBox< Vec3R > &bbox, const GridT &grid)
Definition: RayTracer.h:627
void print(std::ostream &os=std::cout, int verboseLevel=1)
Print parameters, statistics, memory usage and other information.
Definition: RayTracer.h:938
Color shader that treats the surface normal (x, y, z) as an RGB color.
Definition: RayTracer.h:577
void render(bool threaded=true) const
Perform the actual (potentially multithreaded) ray-tracing.
Definition: RayTracer.h:871
const RGBA * pixels() const
Definition: RayTracer.h:326
T lengthSqr() const
Definition: Vec3.h:215
void setCutOff(Real cutOff)
Set the cut-off value for density and transmittance.
Definition: RayTracer.h:199
Accelerated intersection of a ray with a narrow-band level set or a generic (e.g. density) volume...
size_t numPixels() const
Definition: RayTracer.h:325
GridT GridType
Definition: RayTracer.h:73
void over(const RGBA &rhs)
Definition: RayTracer.h:250
Film * mFilm
Definition: RayTracer.h:397
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
BaseShader * copy() const override
Definition: RayTracer.h:665
void setLightDir(Real x, Real y, Real z)
Set the vector components of a directional light source.
Definition: RayTracer.h:177
float ValueT
Definition: RayTracer.h:231
math::Ray< double > getRay(size_t i, size_t j, double iOffset=0.5, double jOffset=0.5) const override
Return a Ray in world space given the pixel indices and optional offsets in the range [0...
Definition: RayTracer.h:438
Film::RGBA & pixel(size_t i, size_t j)
Definition: RayTracer.h:356
RGBA(double _r, double _g, double _b, double _a=1.0)
Definition: RayTracer.h:238
Simple diffuse Lambertian surface shader.
Definition: RayTracer.h:684
RGBA & operator+=(const RGBA &rhs)
Definition: RayTracer.h:248
Vec3R rasterToScreen(double i, double j, double z) const
Definition: RayTracer.h:377
Definition: openvdb/Exceptions.h:13
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:637
BaseShader * copy() const override
Definition: RayTracer.h:699
const RGBA & pixel(size_t w, size_t h) const
Definition: RayTracer.h:273
void lookAt(const Vec3R &xyz, const Vec3R &up=Vec3R(0.0, 1.0, 0.0))
Definition: RayTracer.h:365
BaseShader * copy() const override
Definition: RayTracer.h:643
Vec3< typename promote< T, typename Coord::ValueType >::type > operator+(const Vec3< T > &v0, const Coord &v1)
Allow a Coord to be added to or subtracted from a Vec3.
Definition: Coord.h:525
void setIntersector(const IntersectorT &inter)
Set the intersector that performs the actual intersection of the rays against the narrow-band level s...
Definition: RayTracer.h:828
void setGrid(const GridT &grid)
Set the level set grid to be ray-traced.
Definition: RayTracer.h:820
static double fieldOfViewToFocalLength(double fov, double aperture)
Return the focal length in mm given a horizontal field of view in degrees and the specified aperture ...
Definition: RayTracer.h:458
LevelSetRayTracer(const GridT &grid, const BaseShader &shader, BaseCamera &camera, size_t pixelSamples=1, unsigned int seed=0)
Constructor based on an instance of the grid to be rendered.
Definition: RayTracer.h:769
NormalShader(const Film::RGBA &c=Film::RGBA(1.0f))
Definition: RayTracer.h:601
PositionShader(const math::BBox< Vec3R > &bbox, const Film::RGBA &c=Film::RGBA(1.0f))
Definition: RayTracer.h:656
Definition: Math.h:905
double Real
Definition: openvdb/Types.h:56
math::Ray< double > getRay(size_t i, size_t j, double iOffset=0.5, double jOffset=0.5) const override
Return a Ray in world space given the pixel indices and optional offsets in the range [0...
Definition: RayTracer.h:491
void setScattering(Real x, Real y, Real z)
Set Scattering coefficients.
Definition: RayTracer.h:189
Type Exp(const Type &x)
Return ex.
Definition: Math.h:713
BaseShader * copy() const override
Definition: RayTracer.h:608
BaseShader * copy() const override
Definition: RayTracer.h:561
virtual ~BaseCamera()
Definition: RayTracer.h:354
NormalShader(const GridT &grid)
Definition: RayTracer.h:580
math::AffineMap mScreenToWorld
Definition: RayTracer.h:400
typename GridType::ValueType ValueType
Definition: RayTracer.h:153
void setCamera(BaseCamera &camera)
Set the camera derived from the abstract BaseCamera class.
Definition: RayTracer.h:169
ValueT b
Definition: RayTracer.h:259
ValueT a
Definition: RayTracer.h:259
Vec3< T > unit(T eps=0) const
return normalized this, throws if null vector
Definition: Vec3.h:378
void fill(const RGBA &rgb=RGBA(0))
Definition: RayTracer.h:287
void render(bool threaded=true) const
Perform the actual (potentially multithreaded) volume rendering.
Definition: RayTracer.h:963
Film::RGBA operator()(const Vec3R &xyz, const Vec3R &, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:636
Definition: openvdb/Exceptions.h:65
Film::RGBA operator()(const Vec3R &xyz, const Vec3R &normal, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:583
BaseShader * copy() const override
Definition: RayTracer.h:589
RGBA(ValueT _r, ValueT _g, ValueT _b, ValueT _a=static_cast< ValueT >(1.0))
Definition: RayTracer.h:235
Film::RGBA operator()(const Vec3R &xyz, const Vec3R &normal, const Vec3R &rayDir) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:690
Mat4< double > Mat4d
Definition: Mat4.h:1368
A general linear transform using homogeneous coordinates to perform rotation, scaling, shear and translation.
Definition: Maps.h:309
size_t width() const
Definition: RayTracer.h:323
void setPixelSamples(size_t pixelSamples, unsigned int seed=0)
Set the number of pixel samples and the seed for jittered sub-rays. A value larger than one implies a...
Definition: RayTracer.h:852
ValueT g
Definition: RayTracer.h:259
void savePPM(const std::string &fileName)
Definition: RayTracer.h:298
void setCamera(BaseCamera &camera)
Set the camera derived from the abstract BaseCamera class.
Definition: RayTracer.h:844
Class that provides the interface for continuous sampling of values in a tree.
Definition: Interpolation.h:283
Mat3< typename promote< T0, T1 >::type > operator*(const Mat3< T0 > &m0, const Mat3< T1 > &m1)
Multiply m0 by m1 and return the resulting matrix.
Definition: Mat3.h:611
void postTranslate(const Vec3< T0 > &tr)
Right multiplies by the specified translation matrix, i.e. (*this) * Trans.
Definition: Mat4.h:728
Definition: Math.h:906
ValueT r
Definition: RayTracer.h:259
Definition: Interpolation.h:96
BaseCamera(Film &film, const Vec3R &rotation, const Vec3R &translation, double frameWidth, double nearPlane, double farPlane)
Definition: RayTracer.h:340
A Ray class.
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
T dot(const Vec3< T > &v) const
Dot product.
Definition: Vec3.h:195
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
BaseShader * copy() const override
Definition: RayTracer.h:542
math::Ray< double > mRay
Definition: RayTracer.h:399
MatteShader(const GridT &grid)
Definition: RayTracer.h:533
Shader that produces a simple matte.
Definition: RayTracer.h:530
A (very) simple multithreaded volume render specifically for scalar density.
Definition: RayTracer.h:147
BaseShader()
Definition: RayTracer.h:510
void rayTrace(const GridT &, const IntersectorT &, const BaseShader &, BaseCamera &, size_t pixelSamples=1, unsigned int seed=0, bool threaded=true)
Ray-trace a volume using a given ray intersector.
Definition: RayTracer.h:751
Abstract base class for the shaders.
Definition: RayTracer.h:506
void initRay(double t0, double t1)
Definition: RayTracer.h:390
void setAbsorption(Real x, Real y, Real z)
Set absorption coefficients.
Definition: RayTracer.h:192
DiffuseShader(const GridT &grid)
Definition: RayTracer.h:687
typename GridType::ConstAccessor AccessorType
Definition: RayTracer.h:154
Film::RGBA operator()(const Vec3R &, const Vec3R &normal, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:604
void print(const ast::Node &node, const bool numberStatements=true, std::ostream &os=std::cout, const char *indent=" ")
Writes a descriptive printout of a Node hierarchy into a target stream.
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:178
Definition: Transform.h:39
Abstract base class for the perspective and orthographic cameras.
Definition: RayTracer.h:337
void setLightColor(Real r, Real g, Real b)
Set the color of the directional light source.
Definition: RayTracer.h:180
Film::RGBA operator()(const Vec3R &, const Vec3R &, const Vec3R &) const override
Defines the interface of the virtual function that returns a RGB color.
Definition: RayTracer.h:557