This document was written early in task 1.2. All of the functionality, described herein, has now been implemented within the LightWorks ADS

The LightWorks’ goniometric light source shader currently takes a "file name" argument to define how much light it emits in any one direction. Whilst this is essential functionality, that needs to be maintained, the approach has a number of disadvantages:

Let me introduce the term emission set. An emission set is a data structure which associates a set of scalar values (probably luminous intensities) with a set of directions. Typically, this will be a set of triples: (q, f, phot).

Throughout this document, an emission set will define a luminous intensity distribution. However, the term emission set purposely makes no mention of luminous intensities, so that a future extension of the API can use multiple emission sets (one for each waveband being modelled) to model spectral distributions.

Thus far, this document has only addressed the issue of emission sets. These are fine for data which has come from a physical measuring device, such as a gonioreflectometer, but less suitable for the more artistic end-users of our code.

For example, consider the end-user who wishes to define a light source whose emission varies very rapidly with direction (such as a 2D noise function), or data which is sparse (such as a "disco glitter ball"). The size (memory requirements) of the emission set would have to be very large indeed, if they are to capture all the variation they wish to see. Alternatively, they could save memory and lose data. Neither option seems ideal. Furthermore, if we naïvely store such data in a massive internal array, without any knowledge of where the data comes from, we will end up with poor performance. Whereas, if the person who generated the data was given the option of returning an emission value, they may be able to utilise a number of "fast outs".

The proposal here is that the goniometric light source shader allows procedural definitions of its directional emission function. This would be accomplished in much the same way that procedurally-defined fall-off functions were implemented in LADS’ light source shaders. The goniometric shader would need the following extra arguments:

Callback signature

Shader argument

Description


LtFloat
(LtFuncGonioEmission) (
      LtGenericPtr   data,
      LtDouble       theta,
      LtDouble       phi
)

emission

The procedural definition of the directional emission function.


LtStatus
(LtFuncGonioEmissionNormalise) (
      LtGenericPtr   data,
      LtFloat       *max,
      LtFloat        integral
)

normalise

This is used to normalise the emission function so that non-empirical intensity units can be used.


LtStatus
(LtFuncGonioEmissionFree) (
      LtGenericPtr   data
)

free

Used to free up any memory taken by the data.

The idea is this. The user has a structure of their own, containing whatever data and parameters are needed to supply the emission in a particular direction. They pass the address of this structure to the shader, via the data shader argument.

The user also provides a function (emission) which, given the address of their all-encompassing structure (data), a longitude angle and a latitude angle, will return the appropriate (possibly normalised) luminous intensity value. This means that API users only supply a 2D function, defined over the range [0, 360] x [0, 180]. The positioning and orientation of this emission set is handled via the existing arguments of the goniometric light source shader: "location", "to" and "equator zero"

The second function (normalise) will be called by the goniometric shader as soon as it knows what intensity units it is dealing with. If the user specifies cd or kcd units, then the "intensity" shader argument is interpreted as meaning "the brightest emission from this source, in any direction". If the user specifies lm or klm units, then the "intensity" shader argument is interpreted as meaning "the total power emitted by this source". The purpose of the normalise function is evaluate the maximum value of the (possibly normalised) emission function and the integral of the emission function, over the sphere of all directions. This will enable the goniometric shader to make sense of the data returned by the emission function.

If the normalise function is not provided, then the shader will function consistently, but will not provide physically meaningful results. This is, of course, perfectly acceptable behaviour for users only interested in empirical results.

The default values of these shader arguments are initially NULL for data and free. The emission function is set to LiDefaultGonioEmissionFunc and the normalise function is set to LiDefaultGonioNormaliseFunc: both of these functions expect to be given a regularly parameterised LtEmissionSet to process. When the goniometric shader reads its photometric data from a file, it creates just such an LtEmissionSet and sets the data shader argument to point at it. The shader can then process this data using its default functions. Additionally it sets the free function to LiDefaultGonioFreeFunc which is a function that knows how to tidy up the memory associated with an LtEmissionSet. When the shader is destroyed the free function is called and the memory pointed to by the data argument is freed.

Alternatively, the user may generate an emission set themselves (using LiEmissionSetCreate or LiEmissionSetRead) and pass this to a goniometric shader through the data shader argument. In this case the shader will NOT set the free function and so when the shader is destroyed the emission set is left intact. The user must call LiEmissionSetDestroy for this emission set if they want to free the memory associated with it.

Finally, if an alternative data representation is used (i.e. something other than an LtEmissionSet is passed via the data argument) then it is vitally important that the associated callback functions are also provided. If this is not done then the behaviour of the shader will become unpredictable.

Currently, the situation with the goniometric light source shader is this: The emission set which is got from parsing a file is normalised, and it is the "intensity" argument which determines what the magnitude of the emission is.

Picture a situation where a user wants to get the light's emission magnitude from the file, rather than via our shader args (they have just messed with their IES file so that it has a 1,000,000 cd spike, say). Because of this, we introduced "power from file" shader arg. So we would internally keep track of anything the file said about source power, but actually use whatever was sitting in the "intensity" shader arg. That way, the user could do a shader reset, then Get the "power from file" value, then Set the "intensity" to this value.

With the new API in place, what does the user have to do, to get the power defined in their emission set, actually used? The answer is that their emission set will always be normalized, and the value in the "intensity" shader argument will be used. However, since they know how to normalise their emission set, they know...

It is therefore trivial for the user to set the "intensity" argument to the correct value.

Now that users of the goniometric light source shader are able to define both an emission set, and a filename, we have to define which one takes priority if both are valid. So, let’s do that: If the filename is non-NULL, the shader should attempt to parse it. Failure to parse a file currently results in the light behaving like a point light source (omnidirectional emission). This behaviour should persist, even if they have defined a perfectly valid emission set. If the filename is NULL, the goniometric shader should look at the procedurally-defined interface. If something is fundamentally wrong here, the light should behave like a point light source.

To illustrate the use of the emission and normalise functions with a goniometric light consider the simple example of a light source which is to emit with a random intensity in any given direction. To model such a light using either a photometric file or an emission set would require generating large arrays of random intensities which would waste a lot of memory. Instead the following functions can be defined:



external LtStatus
lw_po_normalise_noise_func(
   LtGenericPtr  the_data,
   LtFloat      *max,
   LtFloat      *integral
)
{
   LtStatus status = LI_STATUS_OK;

   /*
    * The integral is 2PI and the max is 1
    */
   *integral = (LtFloat)6.283185307;
   *max = (LtFloat)1;

   return status;
}

external LtFloat
lw_po_emit_noise_func(
   LtGenericPtr    the_data,
   LtDouble        theta,
   LtDouble        phi
)
{
   return LiRandomFloat();
}

Here the emission function ignores the values of theta and phi passed in and simply returns a random float between 0 and 1 for the intensity. Typically a function would have some dependency on the theta and phi angles passed in.

Because the distribution is normalised the maximum value returned by the normalise function is 1 and the integrated value over the sphere of all directions is returned as 2p (as the expectation value of the random emission function is 0.5).

These functions are then attached to a goniometric shader using the following code segment:


   the_shader = LiShaderCreate(LI_SHADER_CLASS_LIGHT, "goniometric");
   if (the_shader)
   {
      LiDataSetFunc(&data, (LtFunc)lw_po_normalise_noise_func);
      status = LiShaderSetArg(the_shader, "normalise", &data);
      if ( LiStatusOk(status) )
      {
         LiDataSetFunc(&data, (LtFunc)lw_po_emit_noise_func);
         status = LiShaderSetArg(the_shader, "emission", &data);
      }
   }

Note that it is important that the "file name" argument of the shader is not set as this would override the above settings. The results of rendering using such a goniometric shader with an intensity set to 800 lumen inside a square room which is 5m x 5m x 5m can be seen in the following figure.


Figure 1. A cubic room illuminated by the goniometric light created in the above example.

RedLine

Restricted information.

© LightWork Design Ltd. All information contained in
this document is the copyright of LightWork Design Ltd. (unless stated
otherwise) and may not be used or reproduced with prior written consent.

RedLine