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 55: Access to lighting information is based on access to a draw context.

Samples:

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