RenderMan API  23.0
RixIESInline.h
Go to the documentation of this file.
1 /*
2 # ------------------------------------------------------------------------------
3 #
4 # Copyright (c) 1986-2019 Pixar. All rights reserved.
5 #
6 # The information in this file (the "Software") is provided for the exclusive
7 # use of the software licensees of Pixar ("Licensees"). Licensees have the
8 # right to incorporate the Software into other products for use by other
9 # authorized software licensees of Pixar, without fee. Except as expressly
10 # permitted herein, the Software may not be disclosed to third parties, copied
11 # or duplicated in any form, in whole or in part, without the prior written
12 # permission of Pixar.
13 #
14 # The copyright notices in the Software and this entire statement, including the
15 # above license grant, this restriction and the following disclaimer, must be
16 # included in all copies of the Software, in whole or in part, and all permitted
17 # derivative works of the Software, unless such copies or derivative works are
18 # solely in the form of machine-executable object code generated by a source
19 # language processor.
20 #
21 # PIXAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL PIXAR BE
23 # LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
24 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
25 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
26 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. IN NO CASE WILL
27 # PIXAR'S TOTAL LIABILITY FOR ALL DAMAGES ARISING OUT OF OR IN CONNECTION WITH
28 # THE USE OR PERFORMANCE OF THIS SOFTWARE EXCEED $50.
29 #
30 # Pixar
31 # 1200 Park Ave
32 # Emeryville CA 94608
33 #
34 # ------------------------------------------------------------------------------
35 */
36 
37 
38 #ifndef RixIESInline_h
39 #define RixIESInline_h
40 
41 #include <math.h> // for floorf, powf, asinf, atan2f, etc
42 #include <algorithm> // for max, min
43 #include <cfloat> // for FLT_MIN
44 #include <string> // for string
45 #include "RixInterfaces.h" // for RixTexture, etc
46 #include "RixShadingUtils.h" // for F_PI
47 #include "prmanapi.h" // for PRMAN_INLINE
48 #include "ri.h" // for RtColorRGB, RtFloat3, etc
49 
50 PRMAN_INLINE void
52 {
53  // if we haven't read a map...
54  if (!m_iesProfile.profile)
55  {
56  emission->r = emission->g = emission->b = 1.f;
57  return;
58  }
59 
60  float vScale = 0.5f;
61  RtFloat3 localDir = P;
62 
63  // assume projection center is at shader space 0
64  // Transform(P, &localDir, m_profileSpace, 1);
65 
66  if (!m_hasNegativeDeterminant)
67  localDir *= -1.0f;
68 
69  float u;
70  float v;
71  calcLatLongParam(1, (float*)&localDir, m_iesProfile.isExr, &u, &v);
72 
73  if ((u != u) || (v != v))
74  {
75  // FIXME ideally we would want to report an error as opposed to silently
76  // return 1
77  emission->r = emission->g = emission->b = 1.f;
78  return;
79  }
80 
81  if (u < 0.0f || u > 1.0f || v < 0.0f || v > 1.0f)
82  {
83  // FIXME ideally we would want to report an error as opposed to silently
84  // return 1
85  emission->r = emission->g = emission->b = 1.f;
86  return;
87  }
88 
89  // rescale
90  float coneSize = 1.0f;
91  float profileScale = 1.0f;
92  if (m_coneAngle > 0.0f)
93  {
94  coneSize = m_coneAngle / (float)F_PI;
95  profileScale = coneSize;
96  }
97  if (m_iesProfile.profileScale > 0.0f)
98  profileScale = m_iesProfile.profileScale;
99 
100  // on axis = 0, opposite = 1
101 
102  // map the cone to 0:1 on the map
103  v = v * coneSize; // allow overflow to be dealt with by profileScale
104 
105  // profileScale 180 (1.0) = full map < 180 restricts the region
106 
107  v = v / profileScale;
108 
109  // orient against map
110  v = 1.0f - v;
111 
112  v = std::max(std::min(v * vScale, 1.0f), 0.0f);
113 
114  // now exactly on axis == 1, opposite = 0
115 
116  float uf = u * static_cast<float>(m_iesProfile.nu);
117  if (uf >= m_iesProfile.nu)
118  uf -= static_cast<float>(m_iesProfile.nu);
119 
120  int u0 = static_cast<int>(floorf(uf));
121  int u1 = u0 + 1;
122  if (u1 >= m_iesProfile.nu)
123  u1 = 0; // wrap
124 
125  float fu = uf - static_cast<float>(u0);
126 
127  if (u0 < 0 || u0 >= m_iesProfile.nu || u1 < 0 || u1 >= m_iesProfile.nu)
128  {
129  // FIXME ideally we would want to report an error as opposed to silently
130  // return 1
131  emission->r = emission->g = emission->b = 1.f;
132  return;
133  }
134 
135  float vf = v * (static_cast<float>(m_iesProfile.nv) - 1.0f);
136  if (vf >= m_iesProfile.nv)
137  vf = static_cast<float>(m_iesProfile.nv) - 1.0f;
138 
139  int v0 = static_cast<int>(floorf(vf));
140  int v1 = v0 + 1;
141  if (v1 >= m_iesProfile.nv)
142  v1 = m_iesProfile.nv - 1;
143 
144  float fv = vf - static_cast<float>(v0);
145 
146  if (v0 < 0 || v0 >= m_iesProfile.nv || v1 < 0 || v1 >= m_iesProfile.nv)
147  {
148  // FIXME ideally we would want to report an error as opposed to silently
149  // return 1
150  emission->r = emission->g = emission->b = 1.f;
151  return;
152  }
153 
154  int v00 = u0 + v0 * m_iesProfile.nu;
155  int v10 = u1 + v0 * m_iesProfile.nu;
156  int v01 = u0 + v1 * m_iesProfile.nu;
157  int v11 = u1 + v1 * m_iesProfile.nu;
158 
159  float rfu = 1.0f - fu;
160 
161  // clang-format off
162  // Clamp to 0 as some intepolated maps have -ve emission!!
163  if (m_iesProfile.isColor)
164  {
165  emission->r =
166  (1.0f - fv) * ( rfu * m_iesProfile.profile[3 * v00 + 0]
167  + fu * m_iesProfile.profile[3 * v10 + 0])
168  + fv * ( rfu * m_iesProfile.profile[3 * v01 + 0]
169  + fu * m_iesProfile.profile[3 * v11 + 0]);
170  emission->r = std::max(0.0f, emission->r);
171 
172  emission->g =
173  (1.0f - fv) * ( rfu * m_iesProfile.profile[3 * v00 + 1]
174  + fu * m_iesProfile.profile[3 * v10 + 1])
175  + fv * ( rfu * m_iesProfile.profile[3 * v01 + 1]
176  + fu * m_iesProfile.profile[3 * v11 + 1]);
177  emission->g = std::max(0.0f, emission->g);
178 
179  emission->b =
180  (1.0f - fv) * ( rfu * m_iesProfile.profile[3 * v00 + 2]
181  + fu * m_iesProfile.profile[3 * v10 + 2])
182  + fv * ( rfu * m_iesProfile.profile[3 * v01 + 2]
183  + fu * m_iesProfile.profile[3 * v11 + 2]);
184  emission->b = std::max(0.0f, emission->b);
185  }
186  else
187  {
188  emission->r =
189  (1.0f - fv) * ( rfu * m_iesProfile.profile[v00]
190  + fu * m_iesProfile.profile[v10])
191  + fv * ( rfu * m_iesProfile.profile[v01]
192  + fu * m_iesProfile.profile[v11]);
193 
194  emission->r = std::max(0.0f, emission->r);
195  emission->g = emission->b = emission->r;
196  }
197  // clang-format on
198 }
199 
200 PRMAN_INLINE bool
201 RixIES::ReadIESProfile(RixContext const& rixCtx, RtUString const iesProfile)
202 {
203  if (m_iesProfile.profile)
204  m_iesProfile.clear(); // clear existing if we already read data before
205 
206  // AcquireTexture will crash with an empty string...
207  if (!iesProfile.Empty())
208  {
209  static std::string rtxPrefix("rtxplugin:RtxIES?filename=");
210 
211  // FIXME: this doesn't work on Windows...
212  // m_iesProfile.mapName = rtxPrefix + std::string(iesProfile);
213 
214  // FIXME: The unique string construction should be made constant over
215  // the render for performance reasons.
216  std::string mapName = rtxPrefix + std::string(iesProfile.CStr());
217  m_iesProfile.mapName = RtUString(mapName.c_str());
218 
221  // open the texture
222  RixTexture::TxProperties txProps;
223  if (tex->AcquireTexture(m_iesProfile.mapName, RixTexture::AtlasNone,
224  txProps))
225  {
226  msgs->Error(
227  "RixIES emission profile: could not open %s",
228  m_iesProfile.mapName.CStr());
229  m_iesProfile.mapName = US_NULL;
230  return false;
231  }
232  else
233  {
234  msgs->Info(
235  "Building RixIES emission profile(\"%s\")\n",
236  m_iesProfile.mapName.CStr());
237 
238  m_iesProfile.nu = txProps.width;
239  m_iesProfile.nv = txProps.height;
240  m_iesProfile.isExr = false;
241  m_iesProfile.isColor = (txProps.nchannels == 3);
242 
243  // read data buffer
244  m_iesProfile.profile = new float[
245  m_iesProfile.nu * m_iesProfile.nv * txProps.nchannels];
246 
247  RixTexture::TxParams txParams;
248  txParams.nchannels = txProps.nchannels;
249  // txParams.firstchannel = 0; // start at the first channel
250 
251  tex->TextureData(txProps, txParams, 0, 0, m_iesProfile.profile);
252  tex->ReleaseTexture(txProps);
253 
254  int nchans = txProps.nchannels;
255  int nu = m_iesProfile.nu;
256  int nv = m_iesProfile.nv;
257  float total = 0.f;
258  // nv/2 is the extent of the vertical range of the profile, but
259  // because profiles specify data at both the lower boundary and
260  // upper boundary we need to include both the nv = 0 and nv/2 rows.
261  for (int j = 0; j <= nv / 2; ++j)
262  {
263  // The area of a spherical cap is 2*pi*r^2*(1-cos(theta)), where
264  // theta is the polar angle. To get the area of a spherical
265  // strip between two polar angles, we subtract the area of the
266  // smaller cap from the area of the larger one. Our scale
267  // factor is then the ratio of the strip area to the total
268  // surface area of the sphere, so the 2*pi*r^2 factor divided by
269  // 4*pi*r^2 turns into just 0.5.
270  // At the endpoints, we clamp the angle to 0 or pi (since the
271  // strip can't extend past the poles of the sphere) and the
272  // strip ends up being just a cap for those cases.
273  float lowerAngle = (j == 0) ? 0 : F_PI * (2 * j - 1) / nv;
274  float upperAngle =
275  (j == nv / 2) ? F_PI : F_PI * (2 * j + 1) / nv;
276  float scalefactor =
277  0.5f * (cosf(lowerAngle) - cosf(upperAngle));
278  for (int i = 0; i < nu; ++i)
279  {
280  for (int k = 0; k < nchans; ++k)
281  {
282  total += scalefactor *
283  m_iesProfile.profile[((j * nu) + i) * nchans + k];
284  }
285  }
286  }
287  m_normalizationFactor = nu * nchans / total;
288  return true;
289  }
290  }
291  return false;
292 }
293 
294 PRMAN_INLINE float
296 {
297  float res = 1.0f;
298  if (m_coneAngle > 0.0f)
299  {
300  res = 0.0f;
301  RtFloat3 localDir = P;
302  if (m_hasNegativeDeterminant)
303  localDir *= -1.0f;
304 
305  if (localDir.z > 0.0f)
306  {
307  localDir.Normalize();
308  if (localDir.z >= m_cosConeAngle)
309  {
310  res = 1.0f;
311  }
312  }
313  }
314  return res;
315 }
316 
317 PRMAN_INLINE float
319  RtPoint3 const& offsetP, RtVector3 const& segmentDir,
320  float& minT, float& maxT) const
321 {
322  // TODO - this line segment vs cone algorithm comes from David Eberly. We either need
323  // to give appropriate credit, or find an alternative method.
324  float ts = lightN.Dot(offsetP + minT * segmentDir - lightP);
325  float te = lightN.Dot(offsetP + maxT * segmentDir - lightP);
326  if (ts < 0.0f && te < 0.0f) return 0.0f;
327  if (ts * te < 0.0f)
328  {
329  float planeT = lightN.Dot(lightP - offsetP) / segmentDir.Dot(lightN);
330  if (ts < 0.0f)
331  {
332  minT = planeT;
333  }
334  else
335  {
336  maxT = planeT;
337  }
338  }
339  if (m_cosConeAngle < 1e-5f) return 1.0f;
340 
341  float vDn = segmentDir.Dot(lightN);
342  RtVector3 lToP = offsetP - lightP;
343  float c2 = vDn * vDn - segmentDir.Dot(segmentDir) * m_cosConeAngle * m_cosConeAngle;
344  if (c2 == 0.0f) return 0.0f;
345  float lpDn = lToP.Dot(lightN);
346  float c1 = vDn * lpDn - lToP.Dot(segmentDir) * m_cosConeAngle * m_cosConeAngle;
347  float c0 = lpDn * lpDn - lToP.Dot(lToP) * m_cosConeAngle * m_cosConeAngle;
348 
349  float delta = c1*c1 - c0*c2;
350 
351  if (delta > 0.0f)
352  {
353  float rootDelta = sqrtf(delta);
354  float t0;
355  float t1;
356  if (c2 > 0.0f)
357  {
358  t0 = (-c1 - rootDelta) / c2;
359  t1 = (-c1 + rootDelta) / c2;
360  }
361  else
362  {
363  t1 = (-c1 - rootDelta) / c2;
364  t0 = (-c1 + rootDelta) / c2;
365  }
366  assert( t1 >= t0 );
367 
368  minT = std::max(minT, t0);
369  maxT = std::min(maxT, t1);
370  if (minT >= maxT)
371  {
372  return 0.0f;
373  }
374  return 1.0f;
375  }
376  else
377  {
378  return 0.0f;
379  }
380 
381 }
382 
383 // Utility routines
384 PRMAN_INLINE float
385 RixIES::calcLongitude(float x, float y, bool isExr) const
386 {
387  // Compensate for the fact that OpenEXR textures store the
388  // data with a latitude range of [180, -180] from left to
389  // right, while other formats store the data with a latitude
390  // range of [0, 360].
391  float s = atan2f(y, x);
392  if (isExr)
393  {
394  s *= -0.5f / F_PI;
395  if (s < 0.0f)
396  s += 1.0f;
397  }
398  else
399  {
400  s *= 0.5f / F_PI;
401  s += 0.5f;
402  }
403  return s;
404 }
405 
406 PRMAN_INLINE void RixIES::calcLatLongParam(
407  int n, // number of points in BOP
408  float* dir, // array of reflection directions
409  bool openEXR, // Is this an EXR latlong?
410  float* s, // output s array pointer
411  float* t) const // output t array pointer
412 {
413  // this func calculates st values from direction for a lat-long texture map
414 
415  float x, y, z, len, zlen;
416  for (int i = 0; i < n; i++, s++, t++, dir += 3)
417  {
418  /* compute texture coordinates from direction */
419  /* normalize */
420  x = dir[0];
421  y = dir[1];
422  z = dir[2];
423 
424  len = x * x + y * y + z * z;
425  if (len > FLT_MIN)
426  len = 1.f / sqrtf(len);
427 
428  zlen = z * len;
429 
430  /* This clamps the value passed to asinf to avoid
431  * precision issues that might occur in the above
432  * computation. */
433  if (zlen < -1.0f)
434  zlen = -1.0f;
435  if (zlen > 1.0f)
436  zlen = 1.0f;
437 
438  *t = 0.5f - asinf(zlen) * 1.0f / F_PI;
439  *s = calcLongitude(x, y, openEXR);
440  }
441 }
442 
443 #endif
virtual int AcquireTexture(RtUString const fileName, TxAtlasStyle const atlasStyle, TxProperties &txProperties)=0
pxrcore::ColorRGB RtColorRGB
#define US_NULL
void clear()
Definition: RixIES.h:83
Id for New Texture-2D interface.
float * profile
Definition: RixIES.h:94
RtFloat3 RtPoint3
Definition: RiTypesHelper.h:69
PRMAN_INLINE bool ReadIESProfile(RixContext const &rixCtx, RtUString const iesProfile)
Definition: RixIESInline.h:201
PRMAN_INLINE void EvaluateIESProfile(RtFloat3 const &P, RtColorRGB *emission) const
Definition: RixIESInline.h:51
pxrcore::UString RtUString
#define PRMAN_INLINE
Definition: prmanapi.h:86
#define F_PI
pxrcore::Float3 RtFloat3
Definition: RiTypesHelper.h:68
Id for RixMessages interface.
Definition: RixInterfaces.h:97
virtual int TextureData(TxProperties const &txProperties, TxParams const &txParams, int mipLevel, int cubeFace, void *result)=0
float profileScale
Definition: RixIES.h:99
virtual int ReleaseTexture(TxProperties const &txProperties)=0
PRMAN_INLINE float EvaluateConeAngle(RtFloat3 const &P) const
Definition: RixIESInline.h:295
virtual RixInterface * GetRixInterface(RixInterfaceId id) const =0
RtUString mapName
Definition: RixIES.h:93
RtFloat3 RtVector3
Definition: RiTypesHelper.h:71