From 7eb43eb21bbb0b8f29aa1422d8fc92b524ba1bcb Mon Sep 17 00:00:00 2001 From: Anders Langlands Date: Tue, 21 May 2024 12:22:04 -0700 Subject: [PATCH] [hdEmbree] PxrIESFile: add eval() and valid() methods --- pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.cpp | 104 ++++++++++++++++-- pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h | 10 ++ 2 files changed, 107 insertions(+), 7 deletions(-) diff --git a/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.cpp b/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.cpp index 407ae08e67..b05dbe78af 100644 --- a/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.cpp +++ b/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.cpp @@ -6,6 +6,8 @@ // #include "pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h" +#include "pxr/base/gf/math.h" + #include @@ -16,11 +18,43 @@ #define M_PI 3.14159265358979323846 #endif +namespace { + +// ------------------------------------------------------------------------- +// Constants +// ------------------------------------------------------------------------- + +template +constexpr T _pi = static_cast(M_PI); + +constexpr float _hemisphereFudgeFactor = 0.1f; + +// ------------------------------------------------------------------------- +// Utility functions +// ------------------------------------------------------------------------- + + +float +_linearstep(float x, float a, float b) +{ + if (x <= a) { + return 0.0f; + } + + if (x >= b) { + return 1.0f; + } + + return (x - a) / (b - a); +} + +} // anonymous namespace + PXR_NAMESPACE_OPEN_SCOPE bool -PxrIESFile::load(std::string const& ies) // non-virtual "override" +PxrIESFile::load(const std::string &ies) // non-virtual "override" { clear(); if (!Base::load(ies)) { @@ -47,7 +81,8 @@ PxrIESFile::pxr_extra_process() // does the distribution cover the whole sphere? bool is_sphere = false; - if ((*v_angleMax - *v_angleMin) > (M_PI / 2 + 0.1 /* fudge factor*/)) { + if ((*v_angleMax - *v_angleMin) + > (_pi / 2.0f + _hemisphereFudgeFactor)) { is_sphere = true; } @@ -60,19 +95,74 @@ PxrIESFile::pxr_extra_process() float dh = h_angles[h + 1] - h_angles[h]; float dv = v_angles[v + 1] - v_angles[v]; // bilinearly interpolate intensity at the patch center - float i0 = (intensity[h][v] + intensity[h][v + 1]) / 2; + float i0 = (intensity[h][v] + intensity[h][v + 1]) / 2.0f; float i1 = - (intensity[h + 1][v] + intensity[h + 1][v + 1]) / 2; - float center_intensity = (i0 + i1) / 2; + (intensity[h + 1][v] + intensity[h + 1][v + 1]) / 2.0f; + float center_intensity = (i0 + i1) / 2.0f; // solid angle of the patch - float dS = dh * dv * sinf(v_angles[v] + dv / 2); + float dS = dh * dv * sinf(v_angles[v] + dv / 2.0f); _power += dS * center_intensity; } } // ...and divide by surface area of a unit sphere (or hemisphere) // (this result matches Karma & RIS) - _power /= M_PI * (is_sphere ? 4 : 2); + _power /= _pi * (is_sphere ? 4.0f : 2.0f); +} + +float +PxrIESFile::eval(float theta, float phi, float angleScale) const +{ + int hi = -1; + int vi = -1; + float dh = 0.0f; + float dv = 0.0f; + + phi = GfMod(phi, 2.0f * _pi); + for (size_t i = 0; i < h_angles.size() - 1; ++i) { + if (phi >= h_angles[i] && phi < h_angles[i + 1]) { + hi = i; + dh = _linearstep(phi, h_angles[i], h_angles[i + 1]); + break; + } + } + + // This formula matches Renderman's behavior + + // Scale with origin at "top" (ie, 180 degress / pi), by a factor + // of 1 / (1 + angleScale), offset so that angleScale = 0 yields the + // identity function. + const float profileScale = 1.0f + angleScale; + theta = (theta - _pi) / profileScale + _pi; + theta = GfClamp(theta, 0.0f, _pi); + + if (theta < 0) { + // vi = 0; + // dv = 0; + return 0.0f; + } else if (theta >= _pi) { + vi = v_angles.size() - 2; + dv = 1; + } else { + for (size_t i = 0; i < v_angles.size() - 1; ++i) { + if (theta >= v_angles[i] && theta < v_angles[i + 1]) { + vi = i; + dv = _linearstep(theta, v_angles[i], v_angles[i + 1]); + break; + } + } + } + + if (hi == -1 || vi == -1) { + // XXX: need to indicate error somehow here + return 0.0f; + } + + // XXX: This should be a cubic interpolation + float i0 = GfLerp(dv, intensity[hi][vi], intensity[hi][vi + 1]); + float i1 = GfLerp(dv, intensity[hi + 1][vi], intensity[hi + 1][vi + 1]); + + return GfLerp(dh, i0, i1); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h b/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h index a406b62531..3378146df2 100644 --- a/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h +++ b/pxr/imaging/plugin/hdEmbree/pxrIES/pxrIES.h @@ -33,6 +33,16 @@ class PxrIESFile : public pxr_ccl::IESFile { return _power; } + // returns true if the IES files was successfully loaded and processed and + // is ready to evaluate + bool valid() const + { + return !intensity.empty(); + } + + // evaluate the IES file for the given spherical coordinates + float eval(float theta, float phi, float angleScale) const; + protected: // Extra processing we do on-top of the "standard" process() from IESFile void pxr_extra_process();