
For implementers who are using plug-in effects or are overriding the rendering loop, the API provides access to the lighting parameters/attributes which are supported. This includes basic attributes for each respective light type as well as shadow map access.
The information is provided as part of the draw context (MDrawContext). The context is made available at the lowest possible level of granularity, which is the point just before setting up a hardware shader for rendering. Shader parameters can be updated using lighting parameter data, as required, before invoking the shader.
It is possible to use the light and shadow information for fixed-function shading if desired, as the interface is only a data provider which places no restrictions on usage.
The basic construct which returns light property information is a MLightParameterInformation.
A list of these represents the set of active lights in the scene. To determine which lights are active, the 3d viewport parameters for interactive rendering and render globals settings for batch rendering are considered. The number is also restricted by the number of lights in a scene that are considered to be visible as well as the number of lights that can be supported by the active GPU device.
Ambient lights are considered to be a single global ambient with no directional properties. This is a property of the rendering framework and not this API.
The interface for light information is parameter based, as it is for shader instances. As not all lights share the same properties, different parameters are accessible depending on the light type. Semantics provide meaning behind parameter values.
It is possible to access the shadow maps generated by the rendering framework. These are returned as textures (an MTexture). The matrix used to lookup the texture with the appropriate transformation is provided as one of the parameters. Samplers for textures are returned as sampler descriptions. Shadow maps are returned based on viewport or batch render settings as well as per light settings (for example, if shadow mapping is enabled for the light)
The following diagram shows how an MDrawContext is made available at render time for MPxShaderOverride, MSceneRender and MUserOperation operations as well as for MShaderInstance instances.

Figure 65: Access to lighting information is based on access to a draw context.
The following sample code shows how to extract the shadow map parameters for use with OpenGL:
// Get the number of active lights
unsigned int lightCount = context.numberOfActiveLights();
for (unsigned int i=0; i<lightCount; i++)
{
// Get light parameter information for a given light
MHWRender::MLightParameterInformation *lightParam =
context.getLightParameterInformation( i );
if (lightParam) {
MStringArray params;
lightParam->parameterList(params);
// Scan through all the parameters for this light. They may be differ for different
// light types.
for (unsigned int p=0; p<params.length(); p++) {
MHWRender::MSamplerStateDesc samplerDesc;
if (ptype == MHWRender::MLightParameterInformation::kTexture2) {
// OpenGL specific extraction of the GPU handle for a shadow map texture
void *handle = lightParam->getParameterTextureHandle( pname );
if (handle)
int openGLHandle = *((int *)handle));
break;
}
else if (ptype == MHWRender::MLightParameterInformation::kSampler) {
// Get a sampler description.
lightParam->getParameter( pname, samplerDesc );
}
}
}
}
The information in MPassContext provides sufficient information to indicate when a plug-in is being invoked for rendering during shadow map creation. The MPxShaderOverride and MPxDrawOverride interfaces can use this information to either perform less or more complex rendering. By default the renderer itself sets up either default state and / or a default shader for use.
For MPxDrawOverride, the draw code can be optimized to avoid executing code which is only required for color pass rendering. For example any blending operations can be disabled.
For MPxShaderOverride, the draw code may choose to use a custom shadow shader if the color pass shader performs any type of deformation or tessellation.
For convenience, an MShaderInstance can be used from with either plug-in interface. If used from within an MPxShaderOverride, then binding and unbinding should occur at key activation and deactivation time to reduce redundant shader changes. If used from within an MDrawOverride, then binding and unbinding should occur within the draw call.
The resulting shadow maps are used for internal rendering and are made available for beauty pass drawing for plug-ins.
The following is sample code for setting up custom shadow casting and drawing for an MPxDrawOverride. The “beauty pass” uses the shadow map and associated matrix available from a MLightParameterInformation instance.
class hwPhongShaderOverride : public MHWRender::MPxShaderOverride
{
protected:
// Color shader
MHWRender::MShaderInstance *fColorShaderInstance;
// Shadow shader
MHWRender::MShaderInstance *fShadowShaderInstance;
public:
void createShaderInstance()
{
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager();
// If no shadow shader instance created yet acquire one. Use
// the stock shadow shader provided.
if (!fShadowShaderInstance)
{
fShadowShaderInstance =
shaderMgr->getStockShader( MHWRender::MShaderManager::k3dShadowerShader );
}
// If no color shader instance created yet acquire one. For
// now it's just using an internal shader for convenience but
// a custom shader could be written here as well.
if (!fColorShaderInstance)
{
fColorShaderInstance =
shaderMgr->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
}
}
/* virtual */ void activateKey(MHWRender::MDrawContext& context, const MString& key)
{
// Bind color or shadower shader as appropriate
if (fInColorPass)
fColorShaderInstance->bind( context );
else if (fInShadowPass)
{
// Update the parameters on the shadow shader. Use the view projection
// matrix from the active context
MMatrix viewProj =
context.getMatrix(MHWRender::MDrawContext::kViewProjMtx);
fShadowShaderInstance->setParameter("shadowViewProj", viewProj );
fShadowShaderInstance->bind( context );
}
}
// Example of using MShaderInstace to draw. Terminate
// the shader instance here.
/* virtual */ void terminateKey(MHWRender::MDrawContext& context, const MString& key)
{
if (fInColorPass)
fColorShaderInstance->unbind( context );
else if (fInShadowPass)
fShadowShaderInstance->unbind( context );
}
/* virtual */ bool draw(MHWRender::MDrawContext& context,
const MHWRender::MRenderItemList& renderItemList) const
{
// Draw for color pass with a blend state change
if (fInColorPass)
{
stateMgr->setBlendState(sBlendState);
unsigned int passCount = fColorShaderInstance->getPassCount( context );
for (unsigned int i=0; i<passCount; i++)
{
fColorShaderInstance->activatePass( context, i );
MHWRender::MPxShaderOverride::drawGeometry(context);
}
stateMgr->setBlendState(pOldBlendState);
}
// Draw for shadow pass
else if (fInShadowPass)
{
unsigned int passCount = fShadowShaderInstance->getPassCount( context );
for (unsigned int i=0; i<passCount; i++)
{
fShadowShaderInstance->activatePass( context, i );
MHWRender::MPxShaderOverride::drawGeometry(context);
}
}
}
}Even though the number of lights used for internal shading is limited by the renderer's light limit clamp, the API interfaces for accessing light information allow you to specify whether to ignore this light limit setting.
The following interfaces have the option to specify a LightFilter parameter to by-pass the light limit:
Example code using this interface can be found in the dx11Shader (MPxShaderOverride) and viewRenderOverrideShadows (MRenderOverride) plug-ins. The dx11Shader plug-in has code that illustrates invoking an additional refresh on light binding changes.