Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The RIX_BXDFPLUGINCREATE() macro defines the CreateRixBxdfFactory() method, which is called by the renderer to create an instance of the bxdf plugin. Generally, the implementation of this method should simply return a new allocated copy of your bxdf factory class. Similarly, the RIX_BXDFPLUGINDESTROY() macro defines the DestroyRixBxdfFactory()  method  function called by the renderer to delete an instance of the bxdf plugin; a typical implementation of this method function is to delete the passed in bxdf pointer:

Code Block
languagecpp
    RIX_BXDFPLUGINCREATE
    {
        return new MyBxdfFactory();
    }
    RIX_BXDFPLUGINDESTROY
    {
        delete ((MyBxdfFactory*)bxdf);
    }

RixBxdfFactory::BeginScatter()

As mentioned above, integrators invoke RixBxdfFactory::BeginScatter() to obtain a RixBxdf. The renderer's operating model is that the Bxdf that is obtained this way is a closure, with the closure functions being GenerateSampleEvaluateSample, EvaluateSamplesAtIndex(), and EmitLocal. The RixBxdfFactory should stash state in the RixBxdf object and consider that the RixBxdf lifetime is under control of the integrator. Generally integrators will attempt to minimize the number of live  RixBxdf  objects but may nonetheless require a large number. For this reason, the RixBxdf  instances should attempt to minimize  memory consumption and construction / deconstruction costs.

Any computations that the Bxdf need in order to efficient evaluate its closure functions should be computed once inside RixBxdfFactory::BeginScatter(), and then saved in the overridden RixBxdf class. Critically, these computations include upstream evaluation of any pattern networks. Therefore, it is typical for BeginScatter() to invoke  RixShadingContext::EvalParam() in order to evaluate the relevant bxdf input parameters, and then pass the pointers returned from EvalParam to the Bxdf constructor. Since Bxdfs also generally require geometric data, or built-in variables, such as the shading normal , geometric normal, (Nn) and viewing direction (Vn), either BeginScatter() or the Bxdf constructor itself will need to call RixShadingContext::GetBuiltinVar() function for each such built-in variable.

...

Note that at the time of CreateInstanceData, like other shading plugins, Bxdfs are unable to inspect the values of pattern network inputs; therefore, in cases these inputs are provided (i.e: RixParameterList::GetParamInfo() returns k_RixSCNetworkValue) the Bxdf may have no choice but to defer inspection of the inputs until BeginOpacity() or BeginInterior(). At that time, those methods may then choose to return NULL instead.

...

Once a RixBxdf object is obtained, the integrator may invoke the following methods:

  • RixBxdf::GenerateSample()GetEvaluateDomain() to figure out the domain over which the Bxdf evaluate samples;
  • RixBxdf::GenerateSample() to generate  to generate samples of the bxdf function, one sample for each point of the shading context.;
  • RixBxdf::EvaluateSample() to evaluate the bxdf function, one direction for each point of the shading context;
  • RixBxdf::EvaluateSamplesAtIndex() to evaluate the bxdf function, one-or-many directions for a given point of the shading context;
  • RixBxdf::EmitLocal() to retrieve the bxdf's local emission.

...

The RixBxdf methods above are expected to return quantities for each point of the shading context originally given to RixBxdfFactorRixBxdfFactory::BeginScatter(), excepting RixBxdf::EvaluateSamplesAtIndex(), that operates on a single point of the shading context, evaluating the bxdf function for one or many directions.The GenerateSample() function has the following input parameters: transportTrait, lobesWanted, and random number generator. The transportTrait tells the Bxdf the subset of light transport to consider: direct illumination, indirect illumination, or both. lobesWanted specifies what lobes are requested, for example specular reflection, diffuse transmission, etc. The random number generator should be called to generate well

In order to maintain physical correctness, bxdfs are expected to conserve energy and obey the Helmholtz reciprocity principle. Care should be taken so that RixBxdf::GenerateSample(),  RixBxdf::EvaluateSample() and RixBxdf::EvaluateSamplesAtIndex() return consistent results. This allows bxdf plugins to be compatible with different rendering techniques such as unidirectional path tracingbidirectional path tracingphoton mapping and vertex connection merging (VCM).

Sample Generation

The GenerateSample() function has the following input parameters: transportTrait, lobesWanted, and random number generator rng.

  • The transportTrait tells the Bxdf the subset of light transport to consider: direct illumination, indirect illumination, or both. 
  • lobesWanted specifies what lobes are requested, for example specular reflection, diffuse transmission, etc. 
  • rng should be called to generate well-stratified samples; such samples typically reduce noise and improve convergence compared to using uniform random samples. 

The GenerateSample() function has the following output parameters (results): lobeSampled, direction directions, weight weights, forward pdf forwardPdfs, reverse pdf reversePdfs, and compTrans.  lobeSampled is All results are arrays with one value per sample point.

  • lobeSampled is similar to the input lobesWanted, and specifies which lobe

...

  •  was actually sampled.

...

  •  Bxdfs must respect the transportTrait and lobesWanted request and indicate which class of lobe is associated with each sample by setting lobesSampled for each generated sample. If and only if the bxdf is unable to generate a requested sample, then lobesSampled should be marked as invalid by calling SetValid(false); for example, if the lobesWanted argument requests a specific lobe (e.g., diffuse reflection) that the Bxdf does not support because the Bxdf only supports glossy reflections, then lobesSampled[i].SetValid(false) should be called. However, if it is possible to generate the requested samples, then lobesSampled should not be marked invalid and should instead be set to the sampled lobe for each point in the shading context. 

    When generating a Bxdf sample, you can also run into corner cases (often legitimate cases) where a valid sample cannot be generated. For instance, if the camera ray hits the backside of a single-sided object, a valid sample cannot be generated. Therefore, the lobeSampled for this camera ray should also be marked as invalid in this case by calling SetValid(false).

    If an invalid sample is returned to the integrator, the integrator will terminate the ray and avoid any further computation. Bxdfs that do not scatter light (e.g. PxrConstant) should also mark all samples as invalid.

    Note

    There is a subtle difference between an invalid sample and a black sample. If a camera ray hits a diffuse black surface, the sample will have zero contribution to the final image despite being valid. It would make sense for the integrator to terminate the ray because further bounces will not contribute to the final image either, but it is important to splat the black contribution to both the beauty and alpha channel. Failing to do so might result in missing geometry in the rendered image. An invalid sample on the other hand, should not only be terminated but also ignored for splatting purposes.


  • directions is the generated ray direction vectors; these directions must be unit length. 
  • weights is a color per sample indicating that sample's weight. 
  • forwardPdfs should account for light moving from the L to V direction, whereas reversePdfs account for the opposite (from V to L). Bxdfs should always provide both pdf values for integrators to use. Bxdfs

As an example, a purely Lambertian diffuse bxdf should loop over the sample points and for each sample point set the result values as follows: set lobeSampled to diffuse reflection, set the reflection direction to a randomly generated direction chosen with a cosine distribution on the hemisphere defined by the surface normal (using a well-stratified 2D "random" sample value generated by calling the provided random number generator), set the weight to the diffuse surface albedo color times dot(Nn, L) divided by pi, set the forward pdf to dot(Nn, Ln) divided by pi, and set the reverse pdf to dot(Nn, Vn). More details can be found in the source code for the PxrDiffuse bxdf.

The parameters to the EvaluateSample() function are very similar: transportTrait, lobesWanted, random number generator, lobesEvaluated, direction, weight, forward pdf and reverse pdf.  The main difference is that direction (Ln) is an array of inputs that the function should compute weights and pdfs for.

The EvaluateSamplesAtIndex() function is very similar to EvaluateSample(), but is used to evaluate multiple samples at the same surface position and normal, but with different illumination directions (Ln). The inputs are the same as for EvaluateSample(), except that it has two additional inputs: index and number of samples. index is used to index into built-in variables such as the normal Nn and viewing direction Vn, and number of samples is the number of directions (Ln) to evaluate the bxdf for. The functionality of EvaluateSamplesAtIndex()–  the former only exists to make sample evaluation more efficient in certain light transport settings.

...

  • that do not scatter light (e.g. PxrConstant) should

...

  • set both pdfs to zero.
  • compTrans is an optional result which can be used to indicate transmission color; this will be used as alpha in compositing. A bxdf should check that compTrans is not NULL before assigning to it.  

As an example, a purely Lambertian diffuse bxdf should loop over the sample points and for each sample point set the result values as follows: set lobeSampled to diffuse reflection, set the reflection direction to a randomly generated direction chosen with a cosine distribution on the hemisphere defined by the surface normal (using a well-stratified 2D "random" sample value generated by calling the provided random number generator), set the weight to the diffuse surface albedo color times dot(Nn, Ln) divided by pi, set the forward pdf to dot(Nn, Ln) divided by pi, and set the reverse pdf to dot(Nn, Vn). More details can be found in the source code for the PxrDiffuse bxdf.

Sample Evaluation

The parameters to the EvaluateSample() function are very similar to GenerateSample()transportTraitlobesWanted, rnglobesEvaluateddirectionsweightsforwardPdfs and reversePdfs.  The main difference is that directions is an array of inputs that the function should compute weights and pdfs for.

The EvaluateSamplesAtIndex() function is very similar to EvaluateSample(), but is used to evaluate multiple samples at the same surface position and normal, but with different illumination directions (Ln). The inputs are the same as for EvaluateSample(), except that it has two additional inputs: index and number of samples numSamples. index is used to index into built-in variables such as the normal Nn and viewing direction Vn, and numSamples is the number of directions (Ln) to evaluate the bxdf for. The functionality of EvaluateSamplesAtIndex() otherwise is similar to EvaluateSample()and exists in order to make sample evaluation more efficient in certain light transport settings.

Evaluation Domain

Bxdfs can help integrators converge more quickly by providing hints about the domain over which they need to be integrated (the full domain being the entire sphere). This is done by the bxdf implementing a RixBxdf::GetEvaluateDomain() function that returns the appropriate RixBXEvaluateDomain value. For more information, please see Bxdf Evaluation Domain.


RixOpacity

The renderer will invoke the following methods on RixOpacity:

  • RixBxdf::GetPresence() to evaluate the geometry presence.
  • RixBxdf::GetOpacity() to evaluate the opacity color.


...

Additional Considerations

Bxdf Evaluation Domain

Bxdf Lobes

Non-Opaque Surfaces

Alpha for Compositing

...

In order to maintain physical correctness, bxdfs are expected to conserve energy and obey the Helmholtz reciprocity. Care should be taken so that RixBxdf::GenerateSample()RixBxdf::EvaluateSample() and RixBxdf::EvaluateSamplesAtIndex() return consistent results. This allows bxdf plugins to be compatible with different rendering techniques such as:

RixOpacity

The renderer will invoke the following methods on RixOpacity:

  • RixBxdf::GetPresence() to evaluate the geometry presence.
  • RixBxdf::GetOpacity() to evaluate the opacity color.

...

Bxdf Evaluation Domain

Bxdf Lobes

Bxdf Sample Validity

Non-Opaque Surfaces

Alpha for Compositing

Installation

RenderMan will search for bxdf plugins on demand, under the rixplugin searchpath. The following rib stream will search for a plugin file named MyDiffuse.so

Code Block
Bxdf "MyDiffuse" "diffuse1" "color tint" [0.5 0.5 0.5]

Creating a Bxdf args File

If you would like RenderMan for Maya or Katana to recognize your Bxdf and provide a user interface for changing input parameters and connecting output parameters to other nodes, then you will need to create an args file for your bxdf. The args file defines the input and output parameters in XML so that tools like RMS or Katana can easily read them, discover their type, default values, and other information used while creating the user interface for the pattern node. Please consult the Args File Reference for more information.