RenderMan  26.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RixLPEInline.h
Go to the documentation of this file.
1 /*
2 # ------------------------------------------------------------------------------
3 #
4 # Copyright (c) 2023 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 #ifndef RixLPEInline_h
38 #define RixLPEInline_h
39 
40 #include <cstddef> // for NULL
41 #include <map> // for map, etc
42 #include <set> // for set
43 #include <utility> // for pair
44 #include <vector> // for vector
45 #include "RixBxdfLobe.h" // for RixBXLobeSampled, etc
46 #include "RixIntegrator.h" // for RixDisplayServices, etc
47 #include "RixInterfaces.h" // for RixLPEToken, RixCustomLPE
48 #include "RixShadingUtils.h" // for k_ZeroRGB, k_OneRGB
49 #include "prmanapi.h" // for PRMAN_INLINE
50 #include "RiTypesHelper.h" // for RtColorRGB
51 
52 class RixLPE;
53 class RixLPEState;
54 class RixShadingContext;
55 
56 // The number of scattering types: e.g., D1, D2, ..., S1, S2, ...
57 static const unsigned char k_numScatterTypes = k_RixBXMaxNumDiffuseLobes +
58  k_RixBXMaxNumSpecularLobes +
59  k_RixBXMaxNumUserLobes;
60 
61 // Offsets to start of each group
62 static const unsigned char k_diffStart = 0;
63 static const unsigned char k_specStart = k_diffStart + k_RixBXMaxNumDiffuseLobes;
64 static const unsigned char k_userStart = k_specStart + k_RixBXMaxNumSpecularLobes;
65 
70 {
71 public:
72  short getTransition(short state, RixLPEToken symbol)const
73  {
74  const State &mystate = m_states[state];
75  const Transition *begin = &m_trans[mystate.begin_trans];
76  const Transition *end = begin + mystate.ntrans;
77  while (begin < end) { // binary search
78  const Transition *middle = begin + ((end - begin)>>1);
79  if (symbol < middle->symbol)
80  end = middle;
81  else if (middle->symbol < symbol)
82  begin = middle + 1;
83  else // match
84  return middle->state;
85  }
86  return mystate.wildcard_trans;
87  }
88 
89  void * const * getRules(int state, int &count)const
90  {
91  count = m_states[state].nrules;
92  return &m_rules[m_states[state].begin_rules];
93  }
94 
95  struct State
96  {
97  unsigned int begin_trans;
98  unsigned int ntrans;
99  unsigned int begin_rules;
100  unsigned int nrules;
102  std::vector<int> customIdVec;
103  };
104  struct Transition
105  {
106  // we use this only for sorting
107  static bool trans_comp (const Transition &a, const Transition &b)
108  {
109  return a.symbol < b.symbol;
110  }
112  short state;
113  };
114  std::vector<Transition> m_trans;
115  std::vector<void *> m_rules;
116  std::vector<State> m_states;
117 };
118 
119 //
120 // class RixLPE inline method implementation
121 //
122 
123 // Convert an integer light group id to a LPE token.
124 PRMAN_INLINE RixLPEToken
126 {
127  // Offset the light group id by the number of built-in tokens.
128  if (lgtGrpId >= 0)
129  return lgtGrpId + k_baseLgtGrpTokenOffset;
130  else
131  return k_BLANK;
132 }
133 
134 // Return true if there are any light path expression AOVs.
135 PRMAN_INLINE bool
137 {
138  return m_anyLPEs;
139 }
140 
141 // Return true if there are any custom light path expressions.
142 PRMAN_INLINE bool
144 {
145  return m_anyCustomLPEs;
146 }
147 
148 // class RixLPEState implementation
149 
150 PRMAN_INLINE
152  RixLPEAutomata const* automata, const int nautomata )
153  : m_automata( automata ), m_nautomata( nautomata )
154 {
155  Reset();
156 }
157 
158 PRMAN_INLINE
160 {
161 }
162 
163 PRMAN_INLINE void
165 {
166  m_state.resize(m_nautomata);
167  std::fill(m_state.begin(), m_state.end(), 0);
168  m_thruput.One();
169 }
170 
171 PRMAN_INLINE std::vector<short>&
173 {
174  return m_state;
175 }
176 
177 PRMAN_INLINE const std::vector<short>&
179 {
180  return m_state;
181 }
182 
183 PRMAN_INLINE void
184 RixLPEState::SetState(const std::vector<short>& state)
185 {
186  m_state = state;
187 }
188 
189 PRMAN_INLINE bool
191 {
192  if( !m_automata ) return true;
193  for( int i = 0; i < m_nautomata; i++ ) if(m_state[i] >=0) return false;
194  return true;
195 }
196 
197 PRMAN_INLINE bool
199  const RixLPEToken custom)
200 {
201  std::vector<short> saveState = GetState();
202  move(dir, sca, custom);
203  bool active = !Broken();
204  SetState(saveState);
205  return active;
206 }
207 
208 PRMAN_INLINE const RtColorRGB&
210 {
211  return m_thruput;
212 }
213 
214 PRMAN_INLINE const RixLPEAutomata*
215 RixLPEState::GetAutomata(int& nautomata) const
216 {
217  nautomata = m_nautomata;
218  return m_automata;
219 }
220 
221 PRMAN_INLINE void
222 RixLPEState::MoveCamera(RixShadingContext const* sCtx, int sCtxIndex)
223 {
224  PIXAR_ARGUSED(sCtx);
225  PIXAR_ARGUSED(sCtxIndex);
227 }
228 
229 PRMAN_INLINE void
230 RixLPEState::MoveCamera(RixShadingContext const* sCtx, int sCtxIndex,
231  const RtColorRGB& thruput)
232 {
233  PIXAR_ARGUSED(sCtx);
234  PIXAR_ARGUSED(sCtxIndex);
236  m_thruput *= thruput;
237 }
238 
239 PRMAN_INLINE void
241  RixShadingContext const* sCtx,
242  int sCtxIndex,
243  RtColorRGB const& thruput,
244  RixLPEToken lpeGroupId)
245 {
246  PIXAR_ARGUSED(sCtx);
247  PIXAR_ARGUSED(sCtxIndex);
248  PIXAR_ARGUSED(thruput);
249  move( RixLPE::k_OBJECT, RixLPE::k_NONE, lpeGroupId );
250 }
251 
252 PRMAN_INLINE void
254  RixShadingContext const* sCtx,
255  int sCtxIndex,
256  RtColorRGB const& thruput,
257  RtColorRGB const* lightTrans,
258  bool firstContribution,
259  RixLPEToken lgtLpeToken)
260 {
261  PIXAR_ARGUSED(sCtx);
262  PIXAR_ARGUSED(sCtxIndex);
263  PIXAR_ARGUSED(thruput);
264  PIXAR_ARGUSED(lightTrans);
265  PIXAR_ARGUSED(firstContribution);
266  move( RixLPE::k_LIGHT, RixLPE::k_NONE, lgtLpeToken );
267 }
268 
269 PRMAN_INLINE void
271  RixShadingContext const* sCtx,
272  int sCtxIndex,
273  RixLPEScatterEvent const scatterEvent,
274  RixLPEToken lpeGroupId)
275 {
276  PIXAR_ARGUSED(sCtx);
277  PIXAR_ARGUSED(sCtxIndex);
278  if( scatterEvent.GetValid() )
279  {
280  RixLPEToken event = scatterEvent.GetEvent();
281  RixLPEToken scatt = scatterEvent.GetScatt();
282 
283  move( event, scatt, lpeGroupId );
284  }
285 }
286 
287 PRMAN_INLINE void
289  RixShadingContext const* sCtx,
290  int sCtxIndex,
291  RixLPEScatterEvent const scatterEvent,
292  const RtColorRGB& thruput,
293  RixLPEToken lpeGroupId,
294  bool doStateTransition)
295 {
296  PIXAR_ARGUSED(sCtx);
297  PIXAR_ARGUSED(sCtxIndex);
298  if( scatterEvent.GetValid() )
299  {
300  RixLPEToken event = scatterEvent.GetEvent();
301  RixLPEToken scatt = scatterEvent.GetScatt();
302 
303  if( doStateTransition )
304  {
305  move( event, scatt, lpeGroupId );
306  }
307 
308  m_thruput *= thruput;
309  }
310 }
311 
312 PRMAN_INLINE void
313 RixLPEState::move( const RixLPEToken event, const RixLPEToken scatt,
314  const RixLPEToken custom )
315 {
316  if( !m_automata )
317  return;
318 
319  for( int i = 0; i < m_nautomata; i++ )
320  {
321  if (m_state[i] >= 0)
322  m_state[i] = m_automata[i].getTransition(m_state[i], event);
323  if (m_state[i] >= 0)
324  m_state[i] = m_automata[i].getTransition(m_state[i], scatt);
325  if (m_state[i] >= 0)
326  m_state[i] = m_automata[i].getTransition(m_state[i], custom);
327  if (m_state[i] >= 0)
328  m_state[i] = m_automata[i].getTransition(m_state[i], RixLPE::k_STOP);
329  }
330 }
331 
332 
333 //
334 // class RixLPEScatterEvent implementation
335 //
336 
337 PRMAN_INLINE
338 RixLPEScatterEvent::RixLPEScatterEvent(bool isReflect, bool isUser,
339  bool isSpecular, unsigned char lpeId)
340 {
341  m_isReflect = isReflect;
342  m_lpeId = lpeId;
343  if( isUser )
344  {
345  m_scatter = RixLPE::k_USER1 + m_lpeId;
346  m_lpeIndex = static_cast<unsigned char>(k_userStart + m_lpeId);
347  }
348  else if( isSpecular )
349  {
350  m_scatter = RixLPE::k_SPECULAR1 + m_lpeId;
351  m_lpeIndex = static_cast<unsigned char>(k_specStart + m_lpeId);
352  }
353  else
354  {
355  m_scatter = RixLPE::k_DIFFUSE1 + m_lpeId;
356  m_lpeIndex = static_cast<unsigned char>(k_diffStart + m_lpeId);
357  }
358 }
359 
360 PRMAN_INLINE
362 {
363  m_isReflect = lobeSampled.GetReflect();
364  m_lpeId = lobeSampled.GetLpeId();
365 
366  if( !lobeSampled.GetValid() )
367  {
368  m_scatter = RixLPE::k_NONE;
369  m_lpeIndex = 0;
370  }
371  else if( lobeSampled.GetUser() )
372  {
373  m_scatter = RixLPE::k_USER1 + m_lpeId;
374  m_lpeIndex = static_cast<unsigned char>(k_userStart + m_lpeId);
375  }
376  else if( lobeSampled.GetSpecular() )
377  {
378  m_scatter = RixLPE::k_SPECULAR1 + m_lpeId;
379  m_lpeIndex = static_cast<unsigned char>(k_specStart + m_lpeId);
380  }
381  else
382  {
383  // Diffuse
384  m_scatter = RixLPE::k_DIFFUSE1 + m_lpeId;
385  m_lpeIndex = static_cast<unsigned char>(k_diffStart + m_lpeId);
386  }
387 }
388 
389 PRMAN_INLINE
391 {
392  m_isReflect = traits.GetReflect();
393 
394  // Find the lpeId
395  unsigned short dl = traits.GetDiffuse();
396  unsigned short sl = traits.GetSpecular();
397 
398  // Only a maximum of one of these should be set
399  assert((dl && !sl) ||
400  (sl && !dl) ||
401  (!dl && !sl));
402 
403  if( (dl && sl) || (!dl && !sl) )
404  {
405  // Neither set is a supported case. We invalidate the scatter event.
406  // Both set is unsupported. Again we invalidate the scatter event.
407  m_scatter = RixLPE::k_NONE;
408  m_lpeId = m_lpeIndex = 0;
409  return;
410  }
411 
412  // An important point to consider here is that, due to the tests above,
413  // either dl or sl will be non-zero. As a consequence, the following sequence
414  // will never produce an undefined result for m_lpeId because the value
415  // passed to RixFindFirstSetBit will always be non-zero.
416  bool isDiffuse = (dl != 0);
417  if( isDiffuse )
418  m_lpeId = static_cast<unsigned char>(RixFindFirstSetBit(dl));
419  else
420  m_lpeId = static_cast<unsigned char>(RixFindFirstSetBit(sl));
421 
422  m_lpeIndex = isDiffuse ? k_diffStart : k_specStart;
423  m_lpeIndex = static_cast<unsigned char>(m_lpeIndex + m_lpeId);
424 
425  m_scatter = isDiffuse ? RixLPE::k_DIFFUSE1 : RixLPE::k_SPECULAR1;
426  m_scatter += m_lpeId;
427 }
428 
429 PRMAN_INLINE RixLPEToken
431 {
432  return !GetValid() ? RixLPE::k_NONE :
433  (m_isReflect ? RixLPE::k_REFLECT : RixLPE::k_TRANSMIT);
434 }
435 
436 PRMAN_INLINE RixLPEToken
438 {
439  return !GetValid() ? RixLPE::k_NONE : m_scatter;
440 }
441 
442 PRMAN_INLINE unsigned char
444 {
445  return !GetValid() ? 0 : m_lpeId;
446 }
447 
448 PRMAN_INLINE unsigned char
450 {
451  return !GetValid() ? 0 : m_lpeIndex;
452 }
453 
454 PRMAN_INLINE bool
456 {
457  return (m_scatter != RixLPE::k_NONE);
458 }
459 
460 //
461 // class RixLPE::SplatHelper inline method implementation
462 //
463 
464 PRMAN_INLINE
466  RixDisplayServices* displaySvc,
467  int integratorCtxIdx,
468  RixLPE& rixLpe,
469  RixLPEState& state,
470  int depth,
471  RixLPEToken lgtLpeToken,
472  RixLPEToken lpeGrpId,
473  bool isReflect,
474  RtColorRGB const& eyeTrans,
475  RtColorRGB const& lgtTrans,
476  RixShadingContext const* sCtx,
477  int shadingCtxIdx,
478  bool writeOpacityAllowed)
479  : m_depth(depth),
480  m_lgtLpeToken(lgtLpeToken),
481  m_lpeGrpId(lpeGrpId),
482  m_isReflect(isReflect),
483  m_lgtTrans(lgtTrans),
484  m_displaySvc(displaySvc),
485  m_integratorCtxIdx(integratorCtxIdx),
486  m_rixLpe(rixLpe),
487  m_state(state),
488  m_sCtx(sCtx),
489  m_shadingCtxIdx(shadingCtxIdx),
490  m_writeOpacityAllowed(writeOpacityAllowed),
491  m_worldXform(nullptr),
492  m_objectXform(nullptr),
493  m_cameraXform(nullptr)
494 {
495  m_eyeTrans = eyeTrans;
496 
497  if (m_sCtx)
498  {
499  int numMatrices = 0;
500  const RtMatrix4x4* worldXform = nullptr;
501  m_sCtx->GetTransform(Rix::k_current, Rix::k_world, &worldXform, &numMatrices);
502  if (worldXform && numMatrices > 0)
503  {
504  m_worldXform = &worldXform[shadingCtxIdx];
505  }
506  numMatrices = 0;
507  const RtMatrix4x4* objectXform = nullptr;
508  m_sCtx->GetTransform(Rix::k_current, Rix::k_object, &objectXform, &numMatrices);
509  if (objectXform && numMatrices > 0)
510  {
511  m_objectXform = &objectXform[shadingCtxIdx];
512  }
513  numMatrices = 0;
514  const RtMatrix4x4* cameraXform = nullptr;
515  m_sCtx->GetTransform(Rix::k_current, Rix::k_camera, &cameraXform, &numMatrices);
516  if (cameraXform && numMatrices > 0)
517  {
518  m_cameraXform = &cameraXform[shadingCtxIdx];
519  }
520  }
521 }
522 
524 {
525 }
526 
527 PRMAN_INLINE void
529  RixBXActiveLobeWeights& activeLobes,
530  int weightIndex,
531  RtColorRGB const& thruput,
532  bool isFinite,
533  float clamp,
534  bool isHoldout)
535 {
536  // Obtain alpha and write Ci.
537  RixLPE const& rixLpe = m_rixLpe;
538 
539  // Splat LPEs before splat beauty
540  if (rixLpe.m_anyLPEs)
541  {
542  // Light path expressions.
543  bool isReflect = m_isReflect;
544  RixLPEToken lgtLpeToken = m_lgtLpeToken;
545 
546  bool firstContribution = true;
547 
548  // Backup path state, try avoiding memory allocations
549  std::vector<short>& state = m_state.GetState();
550  size_t stateSize = state.size();
551  short stateBuffer[1<<16];
552 
553  short* stateBackup = stateBuffer;
554  if (stateSize > 1<<16)
555  // We seem to have a huge number of automata, we resort to heap memory
556  stateBackup = (short*)malloc(stateSize*sizeof(short));
557 
558  memcpy(stateBackup, state.data(), stateSize*sizeof(short));
559 
560  // Add any non-blank diffuse lobes.
561  for (int i = 0; i < activeLobes.GetNumDiffuseLobes(); i++)
562  {
563  RtColorRGB const& diffuse = activeLobes.GetDiffuseLobe(i)[weightIndex];
564 
565  firstContribution = false;
566 
567  unsigned char lpeId = activeLobes.GetDiffuseLpeId(i);
568  int diffLpeId = k_diffStart + lpeId;
569  RixLPEScatterEvent lpeEvent( isReflect, false, false, lpeId );
570  m_state.MoveVertex(
571  m_sCtx, m_shadingCtxIdx, lpeEvent,
572  m_lpeGrpId);
573  m_state.MoveLight(
574  m_sCtx, m_shadingCtxIdx, diffuse, &m_lgtTrans,
575  firstContribution, lgtLpeToken);
576  SplatLPE(
577  diffuse, &m_lgtTrans, isFinite, clamp, diffLpeId,
578  k_Overwrite, isHoldout);
579 
580  // Restore path state
581  memcpy(state.data(), stateBackup, stateSize*sizeof(short));
582  }
583 
584  // Add any non-blank specular lobes.
585  for (int i = 0; i < activeLobes.GetNumSpecularLobes(); i++)
586  {
587  RtColorRGB const& specular = activeLobes.GetSpecularLobe(i)[weightIndex];
588 
589  firstContribution = false;
590 
591  unsigned char lpeId = activeLobes.GetSpecularLpeId(i);
592  int specLpeId = k_specStart + lpeId;
593  RixLPEScatterEvent lpeEvent( isReflect, false, true, lpeId );
594  m_state.MoveVertex(
595  m_sCtx, m_shadingCtxIdx, lpeEvent,
596  m_lpeGrpId);
597  m_state.MoveLight(
598  m_sCtx, m_shadingCtxIdx, specular, &m_lgtTrans,
599  firstContribution, lgtLpeToken);
600  SplatLPE(
601  specular, &m_lgtTrans, isFinite, clamp, specLpeId,
602  k_Overwrite, isHoldout);
603 
604  // Restore path state
605  memcpy(state.data(), stateBackup, stateSize*sizeof(short));
606  }
607 
608  // Add any non-blank user lobes.
609  for (int i = 0; i < activeLobes.GetNumUserLobes(); i++)
610  {
611  RtColorRGB const& user = activeLobes.GetUserLobe(i)[weightIndex];
612 
613  firstContribution = false;
614 
615  unsigned char lpeId = activeLobes.GetUserLpeId(i);
616  int userLpeId = k_userStart + lpeId;
617  RixLPEScatterEvent lpeEvent( isReflect, true, false, lpeId );
618  m_state.MoveVertex(
619  m_sCtx, m_shadingCtxIdx, lpeEvent,
620  m_lpeGrpId);
621  m_state.MoveLight(
622  m_sCtx, m_shadingCtxIdx, user, &m_lgtTrans, firstContribution,
623  lgtLpeToken);
624  SplatLPE(
625  user, &m_lgtTrans, isFinite, clamp, userLpeId, k_Overwrite,
626  isHoldout);
627 
628  // Restore path state
629  memcpy(state.data(), stateBackup, stateSize*sizeof(short));
630  }
631 
632  if (stateBackup != stateBuffer) free(stateBackup);
633  }
634 
635  // Beauty channel (Ci).
636  if (rixLpe.m_beautyChanId != k_InvalidChannelId)
637  {
638  if (!isHoldout)
639  {
640  RtColorRGB val = isFinite
641  ? activeLobes.SumAtIndex(weightIndex) * thruput * m_lgtTrans * clamp
642  : RixConstants::k_ZeroRGB;
643 
644  SplatBeauty(val, m_eyeTrans);
645  }
646  else if (m_writeOpacityAllowed && (m_depth == 0))
647  {
648  float trans1 = m_eyeTrans.ChannelAvg();
649  if (trans1 != 1.0f)
650  {
651  m_displaySvc->WriteOpacity(
652  m_rixLpe.m_beautyChanId, m_integratorCtxIdx, 1.0f - trans1);
653  }
654  }
655  }
656 }
657 
658 PRMAN_INLINE void
660  RtColorRGB const& emission,
661  RtColorRGB const& thruput,
662  bool isFinite,
663  float clamp,
664  bool isHoldout)
665 {
666  // Obtain alpha and write Ci.
667  RixLPE const& rixLpe = m_rixLpe;
668 
669  // Splat LPEs before splat beauty since we may have a holdout LPE that will
670  // change the Beauty and alpha
671  if (rixLpe.m_anyLPEs)
672  {
673  // Backup path state, try avoiding memory allocations
674  std::vector<short>& state = m_state.GetState();
675  size_t stateSize = state.size();
676  short stateBuffer[1<<16];
677 
678  short* stateBackup = stateBuffer;
679  if (stateSize > 1<<16)
680  // We seem to have a huge number of automata, we resort to heap memory
681  stateBackup = (short*)malloc(stateSize*sizeof(short));
682 
683  memcpy(stateBackup, state.data(), stateSize*sizeof(short));
684 
685  m_state.MoveEmissiveObject(
686  m_sCtx, m_shadingCtxIdx, emission, m_lpeGrpId);
687  SplatLPE(
688  emission, 0, isFinite, clamp, -1, k_Accumulate,
689  isHoldout);
690 
691  // Restore path state
692  memcpy(state.data(), stateBackup, stateSize*sizeof(short));
693 
694  if (stateBackup != stateBuffer) free(stateBackup);
695  }
696 
697  // Beauty channel (Ci).
698  if (rixLpe.m_beautyChanId != k_InvalidChannelId)
699  {
700  if (!isHoldout)
701  {
702  SplatBeauty(
703  isFinite
704  ? (emission * thruput * m_lgtTrans * clamp)
705  : RixConstants::k_ZeroRGB,
706  m_eyeTrans);
707  }
708  else if (m_writeOpacityAllowed && (m_depth == 0))
709  {
710  float trans1 = m_eyeTrans.ChannelAvg();
711  if (trans1 != 1.0f)
712  {
713  m_displaySvc->WriteOpacity(
714  m_rixLpe.m_beautyChanId, m_integratorCtxIdx, 1.0f - trans1);
715  }
716  }
717  }
718 }
719 
720 PRMAN_INLINE void
722  RtColorRGB const& color,
723  bool isFinite,
724  float clamp)
725 {
726  // Splat LPEs before splat beauty since we may have a holdout LPE that will
727  // change the Beauty's alpha
728 
729  if (m_rixLpe.m_anyLPEs)
730  {
731  SplatLPE(color, &m_lgtTrans, isFinite, clamp, -1);
732  }
733 
734  SplatBeauty(
735  isFinite
736  ? (color * m_lgtTrans * clamp)
737  : RixConstants::k_ZeroRGB,
738  m_eyeTrans);
739 }
740 
741 PRMAN_INLINE void
742 RixLPE::SplatHelper::SplatBeauty(RtColorRGB const& val, RtColorRGB& trans) const
743 {
744  m_displaySvc->Splat(m_rixLpe.m_beautyChanId, m_integratorCtxIdx, val);
745 
746  float trans1 = trans.ChannelAvg();
747  if (m_writeOpacityAllowed && (m_depth == 0) && (trans1 != 1.0f))
748  {
749  m_displaySvc->WriteOpacity(
750  m_rixLpe.m_beautyChanId, m_integratorCtxIdx, 1.0f - trans1);
751  }
752 }
753 
754 PRMAN_INLINE void
756  RtColorRGB const& val,
757  RtColorRGB const* lgtTrans,
758  bool isFinite,
759  float clamp,
760  int lpeId,
761  OverwritePolicy overwritePolicy,
762  bool isHoldout)
763 {
764  PIXAR_ARGUSED(lpeId);
765  m_rixLpe.Splat(m_state, m_displaySvc, val, m_integratorCtxIdx, lgtTrans, clamp, isFinite,
766  overwritePolicy, isHoldout, m_worldXform, m_objectXform, m_cameraXform);
767 }
768 
769 #endif