6
\$\begingroup\$

So I'm using ModelMesh and it's built in Effects parameter to draw a mesh with some shaders I'm playing with. I have a simple GUI that lets me change these parameters to my heart's desire. My question is, how do I handle shaders that have unique parameters? For example, I want a 'shiny' parameter that affects shaders with Phong-type specular components, but for an environment mapping shader such a parameter doesn't make a lot of sense. How I have it right now is that every time I call the ModelMesh's Draw() function, I set all the Effect parameters as so

foreach (ModelMesh m in model.Meshes)
        {

            if (isDrawBunny == true)//Slightly change the way the world matrix is calculated if using the bunny object, since it is not quite centered in object space
            {
                world = boneTransforms[m.ParentBone.Index] * Matrix.CreateScale(scale) * rotation * Matrix.CreateTranslation(position + bunnyPositionTransform);
            }
            else //If not rendering the bunny, draw normally
            {
                world = boneTransforms[m.ParentBone.Index] * Matrix.CreateScale(scale) * rotation * Matrix.CreateTranslation(position);
            }

            foreach (Effect e in m.Effects)
            {
                Matrix ViewProjection = camera.ViewMatrix * camera.ProjectionMatrix;
                e.Parameters["ViewProjection"].SetValue(ViewProjection);
                e.Parameters["World"].SetValue(world);
                e.Parameters["diffuseLightPosition"].SetValue(lightPositionW);
                e.Parameters["CameraPosition"].SetValue(camera.Position);
                e.Parameters["LightColor"].SetValue(lightColor);
                e.Parameters["MaterialColor"].SetValue(materialColor);
                e.Parameters["shininess"].SetValue(shininess);

                //e.Parameters
                //e.Parameters["normal"]

            }
            m.Draw();

Note the prescience of the example! The solutions I've thought of involve preloading all the shaders, and updating the unique parameters as needed. So my question is, is there a best practice I'm missing here? Is there a way to pull the parameters a given Effect needs from that Effect?

Thank you all for your time!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I've implemented what you're describing in an open source engine you can download here: quickstartengine.codeplex.com I would post a full answer but I'm on a phone right now and it would take up a couple of pages. Long story short I have XML files defined for my materials, each material describes the shaders it uses, and what values to assign to each parameter. If the parameter is dynamic it will receive those from the game engine. I map each dynamic parameter, like CameraPosition in the same way that you show in your example above. If I get more time I'll try and post a full answer. \$\endgroup\$
    – Nic Foster
    Mar 7, 2012 at 2:15

3 Answers 3

1
\$\begingroup\$

It's possible that in code, you could create a structure that holds the ShaderVariable name and then the ShaderVariable value. You'll most likely need a specific overload or function to set the value, seeing as EffectParameter.SetValue( ... ) only has certain overloads (or you could write a SetValue on the structure that takes a generic object and just does some runtime type determination, but that could be slow).

You could store a list of all these external UniqueEffectParameters with the Model it's used on or with the Effect it's used with. In the end, you'd end up just using another variable along with your UniqueEffectParameter:

foreach (UniqueEffectParameter u in UniqueParameterList) {
    u.SetValue(effect);
}

In order to keep this working without having to have a switch statement or runtime type-checking, SetValue could be a virtual function that you overload with specific classes, say:

public class Vector3EffectParameter : UniqueEffectParameter {
    public Vector3 Value;

    public override void SetValue ( Effect effect ) {
        effect.Parameters[Name].SetValue(Value);
    }
}

The base class would contain just a string Name and an public virtual void SetValue for you to override and use as you like. I hope this makes sense. Good luck!

\$\endgroup\$
1
\$\begingroup\$

You could do something like this:

  1. At startup, you run though all the Effects and save the parameters name that they expose
  2. Create a class that let the user associate a parameter name (String) with a resource (Textures, matrix ...) (for example, he can say that the string "ColorMap" is associated with the texture Diffuse123)
  3. When you draw the model, you ask for this class to lookup and see if the current effect has any of this exposed properties globally setted by the user and if it has you just update the related effect parameter, for example, if the current shader has a parameter called "ColorMap", the above class will recover the texture previously added and will """inject""" it in the effect.

In XNA, every Effect have an EffectParameterCollection, that contains lots of EffectParameter that have a Name and methods to set and get their values. Take a look at it.

Lots of engine like Hieroglyph 3 (Directx 11) has some way of handling global effect parameters.

\$\endgroup\$
0
\$\begingroup\$
foreach(var parameter in e.Parameters)
{
    Debug.WriteLine(parameter.Name);
}
\$\endgroup\$
1
  • 4
    \$\begingroup\$ Some explanation as to what this does is good. I can tell, but it's typically good to post some text with your answers and not just code. \$\endgroup\$
    – House
    Mar 28, 2012 at 21:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .