Introduction

RixShadingContext encapsulates a shading context - a description of the environment in which shading is to be performed. It is the primary domain of operation for the shader classes RixBxdf RixDisplacement RixLight RixPattern, and RixVolume, and is also of high importance to RixIntegrator. Implementors of these classes and their associated plugins will thus need to be intimately familiar with the RixShadingContext class.

A  RixShadingContext usually represents a collection of one or more geometric points that may be shaded. The group of points may arrive via ray tracing hits on geometry, or may be associated with tessellated micropolygons. In either case, shaders will make use of the shading context primarily in one of two ways: inquiring about geometric information through the GetPrimVar() and GetBuiltinVar() methods, and evaluation of the input parameters to the shading function is provided via the EvalParam() methods.

Generally, a shader should consider its associated shading context to be const read only. Shaders that need to write to the shading context will need to use a mutable shading context.

Fields

A RixShadingContext contains several fields that will be filled in by the renderer or integrator and may contain useful information pertinent to shading computations. For all shader types, this information should be considered read-only, which is enforced through the use of a const RixShadingContext. RixIntegrator plugins may be required to fill in some of these fields through the course of their operation.

Builtin Variables

Builtin variables represent values which are usually always present for all geometry types, and are often used in fundamental shader calculations. Some variables like P, the shading position, and Nn, the normalized shading normal, are derived from the geometry definition. Others like incidentRaySpread and VLen are derived from the incoming rays or from a combination of the geometry definition and the incoming rays.

Builtin variables may be queried using the GetBuiltinVar() method. The BuiltinVar enumeration is used to select one of the following variables:


BuiltinVar must be passed a pointer to a pointer of the appropriate type for the variable. The returned pointer points at const storage containing the value of the variable, one value for each point in the RixShadingContext. This storage is owned by the renderer and is valid for the duration of the shader execution.

For normal shading contexts, SetBuiltinVar() is a no-op. Shaders that wish to change the values of builtin variables will need to create a mutable shading context.

The following diagram illustrates several fundamental builtin variables: the surface position P, the normalized shading normal Nn and the normalized shading tangent Tn, the geometric normal Ngn, and the parametric derivatives dPdu and dPdv.



The following diagram illustrates the incidentRaySpread and incidentRayRadius properties of the ray which are related to ray differentials, and are important to the computation of filter widths for antialiasing.



The next diagram illustrates an incident ray and potential reflected rays and transmitted rays that may be returned by a Bxdf as the next indirect ray fired by an integrator. Here, the green lines represent a side view of the tessellated micropolygon geometry. Note that the geometric normal Ngn and the micropolygon size mpsize is derived directly from the tessellated representation, while the biasR and biasT variables can be used to bias the origins of the indirect rays.


Primitive Variables

Primitive variables (often referred to as primvars) represent values which may be optionally present on the geometry. These primvars are uniquely named and may be supplied with the geometry definition. Shaders are usually written knowing the name and data type of certain primvars in advance, such as texture coordinates. However, since the primvars are optional, shaders are expected to fail gracefully if the primvars are missing.

The first variant of GetPrimVar() can be used to efficiently query the existence, type, and array length of a primvar. The other variants return a pointer to const storage containing the value of the primvar in the var output parameter, if the primvar exists. If the primvar cannot be found, storage will still be allocated and will be filled with the value of the input fill parameter. The optional radius output parameter if supplied will point at storage containing the approximate isotropic filter radius associated with the primvar. This value is computed by the renderer from the ray differentials and can be used to perform any necessary shader antialiasing.

There are also variants of GetPrimVar() which return the partial derivatives of the primvar measured with respect to the builtin variables u and v. These methods may be used in certain conditions that are otherwise hard to filter correctly.

For normal shading contexts, SetPrimVar() is a no-op. Shaders that wish to change the values of primitive variables will need to create a mutable shading context.

Parameter Evaluation

Evaluation of the input parameters to the shader is provided via the EvalParam() methods. Input parameters may be constant (the direct parameters to the shader invocation), or may be connected to an upstream node and trigger an upstream of that node (and in turn, any of its dependencies). All such upstream computation will be automatically cached by the renderer for the duration of the RixShadingContext.

The desired input parameter to the shader is selected by an integer paramId, which is the ordinal position of the parameter in the parameter table returned by RixShadingPlugin::GetParamTable(). Callers are expected to know the paramId, the type of the associated parameter and are expected to pass a pointer to a pointer of the appropriate type to EvalParam().

If the value cannot be obtained from the shading network or from the direct parameters associated with the shader invocation, the result will be filled with the default value provided in dflt.

Depending on whether the input parameter is the default value, or a constant value or a connection, the call to EvalParam() will either return a pointer to storage containing a single value or an entire numPts worth of values. This can be changed by setting the optional parameter promoteToVarying to be true, in which case the storage for the result will be guaranteed to be an entire numPts worth of values; however, this does not alter the return value.

The return value of EvalParam is a value of RixSCDetailk_RixSCUniform indicating that the input parameter was uniform (a constant or default value) or k_RixSCVarying indicating the parameter was varying (a connection). If the value cannot be found (typically because it does not have the correct matching type, i.e. the wrong version of EvalParam was used) then k_RixSCInvalidDetail will be returned.

In some circumstances, users of EvalParam() that do not know the paramId (perhaps due to the use of a dynamic parameter table) or the type of the parameter can introspect for the paramId or information about the parameter via the GetParamId() and the GetParamInfo() methods.  

Memory Management

RixShadingContext provides a fast memory allocation service tailored to shaders that need to execute quickly. The main provider of this service is the Allocate() routine; the New() routine and the class Allocator are wrappers around Allocate(). The backing store for this memory allocation are memory pools which are tailored specifically to the lifetime requirements of RixPattern and RixBxdf, with the latter category equating to the the lifetime of the RixShadingContext. Clients that use these memory management services should use "placement new" semantics and should not rely on the invocation of a destructor.