API Guide > Viewport 2.0 API > Maya Viewport 2.0 API Guide > 4. Advanced Topics >

# 4.2 Lighting Interfaces

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.

Samples:

- The dx11Shader SDK plug-in has sample code which parses semantics from an HLSL file. When updating lighting parameters, it accesses the lighting information on the MDrawContext provided and binds the appropriate parameters to the light parameters on a given HLSL shader.
- The hwPhongShader MPxShaderOverride has simple code which demonstrates how to query light information.
- The gpuCacheDrawOverride MPxDrawOverride shows how the lighting information can be used to set up fixed function lighting using a simpler interface which is not parameter based.

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 );
                }
            }
        }
    }

## 4.2.1 Shadowing Control

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);
                }
            }
        }
    }

The dx11Shader example uses custom shaders.

## 4.2.2 "Unlimited" lighting information access

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:

- MDrawContext::numberOfActiveLights()
- MDrawContext::getLightInformation()
- MDrawContext::getLightParameterInformation()

## 4.2.3 Shadow / lighting updates "on demand"

- It is possible to ask to compute shadow maps on specific light DAG objects even if they are beyond the Viewport 2.0 renderer’s light limit setting. The method MRenderer::setLightRequiresShadows() can be used to either queue or remove from queue such a request. This method does not force a given shadow map to be computed, nor does it force any refresh or new render to occur. It triggers a check for the need for a shadow map update. MRenderer::setLightsAndShadowsDirty() (see Change Management) can be used to force computation, if necessary.
- If this method is called from within the rendering of a frame, then any shadow map computation update occurs on the next scene update. For example, if this is called from within a hardware shader plug-in (MPxShaderOverride), then updates occur in the next frame (refresh). If called from a render override operation, then the next scene render triggers an update. The scene render is either the next MSceneRender called from within an override, or the next scene render called by the renderer internally when there is no render override.

- 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.

- Generally, lighting information is only updated during a scene render (MSceneRender). If a shadow request is performed from within a user operation (MUserRenderOperation) in a custom render override, then only the last available lighting information is available. If a user operation requires lighting information, the new virtual method MUserRenderOperation::requiresLightData() can be overridden and set to return true.
- To allow for a scene render in a render override to access information such as shadow maps after update, two methods can be used: MSceneRender::preSceneRender() and MSceneRender::postSceneRender(). They are called before and after either: shadow map update, or the rendering of the scene. At this time, the current draw context is available, and hence lighting/shadowing information (MLightParameterInformation).
- An example which uses the new interfaces is the new viewRenderOverrideShadows render override plug-in. This plug-in performs a user operation to queue shadow requests and then uses them in a custom scene render. The pre-scene render method is overridden to extract the requested shadow maps for use during scene rendering by a custom shader. Light and shadow information beyond the light limit can be accessed.