Date: Thu, 28 Mar 2024 20:32:53 +0000 (UTC) Message-ID: <572730556.8236.1711657973230@ip-10-0-0-233.us-west-2.compute.internal> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_8235_1031610638.1711657973229" ------=_Part_8235_1031610638.1711657973229 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
This documentation is intended to ins=
truct developers in the authoring of custom lights. Developers should also consult the =
RixLight.h
header file=
for complete details.
The RixLightFactory
interface is a subclass of R=
ixShadingPlugin
, and defines a shading plugin responsible for creati=
ng a RixLight
object.
The RixLight
interface characterizes the light emittin=
g from an analytic light source - a light source that can be described prog=
rammatically or by a formula.
RixLightFactory is a subclass of RixShadingPlugin,
and therefore shares the same initialization, synchronization, and parameter table logic as other shading plugins.&n=
bsp; Therefore to start developing your own Light, you can #include "=
RixLight.h"
and make sure your light factory class implements t=
he required methods inherited from the RixShadingPlugin interface: I=
nit()
, Finalize()
, Synchroni=
ze()
, GetParamTable()
, and Cr=
eateInstanceData()
. Generally, there is one shading plugin=
instance of a RixLightFactory
per bound RiLight (RIB) request. This instance may be active in multiple threads si=
multaneously.
The RIX_LIGHTFACTORYCREATE() macro defines the CreateRixLightFactory() f= unction, which is called by the renderer to create an instance of the light= factory plugin. Generally, the implementation of this method should simply= return a new allocated copy of your light factory class. Similarly, the RI= X_LIGHTFACTORYDESTROY() macro defines the DestroyRixLightFactory() function= called by the renderer to delete an instance of the light factory plugin; = a typical implementation of this method is to delete the passed in light fa= ctory pointer:
RIX_LIG= HTFACTORYCREATE { return new MyLightFactory(); } RIX_LIGHTFACTORYDESTROY { delete ((MyLightFactory*)factory); }
RixLight
is the abstract base class from which you can deri=
ve your own light implementations. To illustrate the API, we have provided =
PxrSimpleRectLight.cpp, which implements a simple non-textured single-sided=
light of rectangular shape. Note that the PxrRectLight that ships with Ren=
derMan offers more features than illustrated here, and uses more sophistica=
ted sampling strategies. It also supports bidrectional sampling and photon =
emission, which the example does not.
The light's constructor is called by the corresponding PxrSimpleRe=
ctLightFactory
.
We have two methods used to communicate geometric properties to the ray =
tracer. GetBounds()
returns a sequence of points describing th=
e bounding shape of the light. The bounds should be expressed in the local =
space of the light. For our rect light example, there are four points in th=
e range +/- 0.5 in x and y. The rect light lies on the z=3D0 plane. Interse=
ct, the second method, will compute an intersection between the light and a=
n incoming ray. The intersection is computed in the local space of the ligh=
t. A consequence of this is that the ray direction will not be normalized i=
f the light's transform contains a scale. It's important, therefore, not to=
make use of any optimisations in your intersection function that does assu=
me a unit length direction.
There are three methods that act as helpers for the renderer's light sel= ection scheme. Light selection is a stochastic process whereby, according t= o integrator settings, one or more lights are assigned to a shade point in = a rendering iteration. The lights that are selected have samples generated = for them (see below). The purpose of selection is to attempt to choose the = lights liable to contribute most to the shade point in question, thereby ke= eping variance low.
virt= ual RtFloat GetIncidentRadianceEstimate( RtPoint3 const& P, RtMatrix4x4 const& lightToCurrent, RtMatrix4x4 const& currentToLight) const =3D 0;
To help with this calculation, the renderer will call GetIncidentR=
adianceEstimate()
on the light, providing both the position of the s=
hade point (in 'current' space) and a pair of transforms. In our RectLight =
example, we check to see if the shade point lies to the front of the light.=
If it does, we multiply its intensity by its area (which may be non-unity =
in the event of a scale transform) and the cosine of the angle between its =
normal and the vector between shade point and light center. We then divide =
by the squared distance to the light center and return the result.
virt= ual RtFloat GetIncidentRadianceEstimate( RtPoint3 const& segmentOrigin, RtVector3 const& segmentDir, RtFloat segmentLen, RtMatrix4x4 const& lightToCurrent, RtMatrix4x4 const& currentToLight, RtFloat& minT, RtFloat& maxT) const =3D 0;
A second overload of GetIncidentRadianceEstimate()
is =
used to compute estimates for ray segments rather than individual points. T=
his is used exclusively for equiangular sampling of volumes. In our example=
, we find the nearest point on the incoming line segment to the light and t=
hen treat that just as the shade point in the simpler case. Note that this =
overload has minT and maxT as return values. These can be used to 'clip' th=
e line segment, providing a subset over which the light provides non-zero i=
llumination. For example, since the rect light is single-sided, we could cl=
ip the segment against the light's plane. Similarly, if the light was a spo=
t light, we could clip the segment against the cone's frustum.
virt= ual float GetPowerEstimate(RtMatrix4x4 const& xform) const =3D 0;
GetPowerEstimate()
should return the light's intensity by i=
ts area. This is a crude estimate given independent of any shade point.
stru= ct GenerateSamplesResults { public: int& patchIndex; // only set by mesh lights RtFloat3& UVW; RtVector3& direction; float& distance; float& pdfDirect; bool const isBidirectional; float& pdfEmit; float& pdfEmitDirection; float& solidAngleToArea; RtColorRGB diffuseColor; RtColorRGB specularColor; RtNormal3& normal; }; virtual void GenerateSamples( RixLightContext const& lCtx, RixScatterPoint const& scatter, GenerateSamplesResults& results) const =3D 0;
GenerateSamples()
is the function used to create a sample o=
n the light and put it in the GenerateSamplesResult
structure,=
defined in RixLight.h. UVW
indicates the position of the samp=
le in the light's parametric space; direction is the normalized vector from=
the shade point to the light sample position in 'current' space; distance =
is the distance between the two points; and pdf is the pdf of the chosen po=
int in solid angle measure. In the example case, we have a uniform probabil=
ity of sampling across the light's surface, so the area pdf is 1/area. This=
is then converted to solid angle measure by multiplying by the cosine of t=
he angle between light and outgoing direction, and dividing by the squared =
distance. The light returns both radiance in both diffuseColor
=
and specularColor
. These will be interpreted separately by a =
bxdf's diffuse and specular lobes, and allows for a light to contribute dif=
ferent radiances for each. The light should also return the local-space nor=
mal at the sampled point on the light. (The normal is constant in the examp=
le rect light.) Note that the input RixLightContext
grants the=
function access to the sample's time in normalized shutter time (ie 0 at s=
hutter open and 1 at shutter close); a function GetLightToCurrentTran=
sform()
will return a matrix at the appropriate time, and gives acce=
ss to a random-number pair in a well-stratified sequence. A flag on the solidAngleToArea
is a =
conversion factor to convert between the two pdf measures. For a rect light=
, this would be the cosine of the angle between light normal and the direct=
ion vector divided by the squared distance. pdfEmit
is the pro=
bability of emitting a photon from the selected sample position on the ligh=
t, again expressed in a solid angle measure. (For a rect light with a unifo=
rm sampling scheme, pdfEmit
would be 1/area.) pdfEmitDir=
ection
is the probability of emitting a photon in the selected direc=
tion given the selected sample position. (For a rect light with cosine emis=
sion distribution, this would be cos(theta) / PI.)
stru= ct EvaluateSamplesResults { float& pdfDirect; bool const isBidirectional; float& pdfEmit; float& pdfEmitDirection; float& solidAngleToArea; RtColorRGB diffuseColor; RtColorRGB specularColor; RtNormal3& normal; }; virtual void EvaluateSamples( RixLightContext const& lCtx, RixSamplePoint const& sample, RixScatterPoint const& scatter, EvaluateSamplesResults& results) const =3D 0;
EvaluateSamples()
is called so that the light can compute i=
ntensity and angular-measure pdf for an incoming ray direction (typically g=
enerated by sampling a Bxdf). EvaluateSamples()
will only be c=
alled for a ray if a previous Intersect call returned true for the same ray=
. Results are returned in the EvaluateSamplesResult
structure,=
definied in RixLight.h. 'pdfDirect
' is the solid-angle-measur=
e pdf for the ray; diffuseColor
and specularColor
=
are the light's contribution for diffuse and specular lobes respectively, =
and 'normal' is the light's surface normal at the point of intersection. Th=
e bidirectional result quantities are the same as described above for GenerateSamples()
.
stru= ct GenerateEmissionResults { int& patchIndex; // only set by mesh lights RtFloat3& UVW; RtPoint3& position; RtNormal3& normal; RtVector3& direction; float& distance; float& pdfEmit; // area measure float& pdfEmitDirection; }; virtual void GenerateEmission( RixLightContext const& lCtx, GenerateEmissionResults& results) const =3D 0;
GenerateEmission()
is the function used to create photons f=
rom the light, used in a bidirectional pathtracing context. Note that it re=
quires four random numbers: two for picking a point on the surface (with un=
iform probability in our example) and two for picking a direction (with a c=
osine distribution). Note that in this special case, since we don't at this=
stage in the process of a shade point, the pdfs are not in the solid angle measure. We return pdfEmit
and pd=
fEmitDirection
(see above) and the renderer will employ a solid-angl=
e-measure conversion once the emitted photon has struck a surface internall=
y.
stru= ct EvaluateEmissionForCameraResults { RtColorRGB cameraColor; }; virtual void EvaluateEmissionForCamera( RixLightContext const& lCtx, RixSamplePoint const& sample, RixScatterPoint const& scatter, EvaluateEmissionForCameraResults& results) const =3D 0;
EvaluateEmissionForCamera()
will be called if a light is ma=
rked as camera-visible and is intersected by a camera ray. Its result is re=
turned in the EvaluateEmissionForCameraResults
structure, whic=
h contains the single color field cameraColor
.
vir= tual RixLight* Edit( RixContext& ctx, RtUString const name, RixParameterList const* pList, RtPointer instanceData) =3D 0;
Edit()
is the function that will be called after any change=
s are made to the light properties. It is expected to update the class memb=
ers for any subsequent sampling. Note that in more sophisticated lighting e=
xamples, this could involve such things as computing a new CDF table for a =
textured light.