Click or drag to resize
DigitalRuneHow To: Add Support for New Effect Parameters

DigitalRune Graphics automatically creates bindings for the most common parameters in DirectX Effect files. This topic demonstrates how to add support for new types of parameters.

This topic contains the following sections:

Problem

Let's assume that the graphics engine needs to support a new type of effect parameter. A new material needs a random value. The effect parameter is of type float and the value should always lie between 0 and 1. The value should be evaluated "per instance". That means, each mesh instance should use a different value in the shader.

The effect parameter may be declared in different ways in the effect files (.fx). The parameter name may be "RandomValue". For example:

float RandomValue;

Or the effect parameter can have a semantic "RANDOMVALUE". For example:

float Value : RANDOMVALUE;

In either case, the engine should automatically recognize the effect parameter and set a random value at runtime.

Solution

First, a new IEffectInterpreter needs to be implemented:

C#
using System;
using DigitalRune.Graphics.Effects;
using Microsoft.Xna.Framework.Graphics;

namespace MyGame
{
  class CustomEffectInterpreter : IEffectInterpreter
  {
    public EffectTechniqueDescription GetDescription(Effect effect, EffectTechnique technique)
    {
      return null;
    }

    public EffectParameterDescription GetDescription(Effect effect, EffectParameter parameter)
    {
      // This method is called for each effect parameter when a new effect is loaded.
      // Let's look for float parameters that are named "RandomValue" or have a semantic
      // "RandomValue".
      if (parameter.ParameterClass == EffectParameterClass.Scalar 
          && parameter.ParameterType == EffectParameterType.Single
          && (string.Equals(parameter.Name, "RandomValue", StringComparison.OrdinalIgnoreCase)
             || string.Equals(parameter.Semantic, "RandomValue", StringComparison.OrdinalIgnoreCase)))
      {
        // Parameter found. Let's set a new effect parameter description.        
        return new EffectParameterDescription(parameter, "RandomValue", 0, EffectParameterHint.PerInstance);
      }

      return null;
    }
  }
}

Second, a new IEffectBinder needs to be implemented:

C#
using System;
using System.Collections.Generic;
using DigitalRune.Graphics;
using DigitalRune.Graphics.Effects;
using Microsoft.Xna.Framework.Graphics;

namespace MyGame
{
  class CustomEffectBinder : IEffectBinder
  {
    private static Random Random = new Random(12345);

    public EffectTechniqueBinding GetBinding(Effect effect)
    {
      return null;
    }

    public EffectParameterBinding GetBinding(Effect effect, EffectParameter parameter, IDictionary<string, object> opaqueData)
    {    
      var description = effect.GetParameterDescriptions()[parameter];
      if (description.Semantic == "RandomValue")
        return new DelegateParameterBinding<float>(effect, parameter, GetRandomValue);

      return null;
    }

    private static float GetRandomValue(DelegateParameterBinding<float> binding, RenderContext context)
    {
      return (float)Random.NextDouble();
    }
  }
}

The effect interpreter and binder need to be registered in the graphics service:

C#
_graphicsManager.EffectInterpreters.Insert(0, new CustomEffectInterpreter());
_graphicsManager.EffectBinders.Insert(0, new CustomEffectBinder());

Now, whenever a new effect is loaded, the new effect interpreter is called and searches for the new type of parameter. If a matching parameter is found, a new parameter description is set.

Once all effect parameters are identified, the new effect binder is called. It searches for a parameter with the newly created description. If a matching parameter is found, it creates a new parameter binding.

During rendering the effect parameter binding is evaluated for each visible mesh node. The binding creates a random number and sets the parameter value.

See Also