5
\$\begingroup\$

The title explains my two biggest logical problems with developing a game. It's something I sit up at night thinking about, and that is "What is the most effective way to manage shaders" and "What is the most effective way to manage objects (I call them entities in my game)"

Before I actually began to create I game, I made a bunch of simple projects, and my shader and object management systems have always felt flimsy to me. The methods I usually used for shaders were:

  1. Preload all the shaders when the game loads
  2. All the shaders are based off of "ShaderBase"
  3. Each class holds a pointer to a class derived from "ShaderBase"
  4. When the object is rendered, the app calls the MapData and Render functions of the ShaderBase derived class that the objects pointer contains

This had some problems, however. First of all, is I usually created a different shader when the entities that use it needed special information (Perhaps it was a water shader, and it would be tessellated, so it needed that information). I would need to map different data types to the shader. That means I'd need different versions of ShaderBase's MapData function, for example a regular entity would use:

void ShaderBase::MapData(TransformBuffer transBuf, LightingBuffer lightBuf);

but the water would use:

void ShaderBase::MapData(TransformBuffer transBuf, LightingBuffer lightBuf, WaterDataBuffer waterBuff);

Having to do this seemingly ruins the point of inheritance, because my base class would soon be loaded with different functions for different shaders.


Now the methods for object/entity management. I didn't really have any problems, I just felt like maybe there was a better way to do it. Here's this process:

  1. Create all the objects (Maybe from a world file)
  2. Sort all the objects by purpose and/or shader, to cut down on state changes
  3. A class called EntityList would have an array of all the entities, and each frame would run through that array, and call each ones Render() function, and if it is time, a Think() function (Because some entities may think in steps). It also places the object in its rightful location in the world.

I can see how things like instancing can also be applied here to cut down on resource usage. However, I just have a feeling theres a better way to do this. Each entity is derived from EntityBase, so theres no real problems with inheritance. I might be wrong, but I don't want to continue and have to redo this later.


I didn't mention this above, but I have a sort of bonus question that came to mind while I was writing this:

With the new DirectX 11 features in shaders, what method is better to use: Using techniques, passes, etc. and effect (.fx) files, or the seemingly newer method (I actually don't know when it came about) with each shader in an individual file (.PS, .VS, etc.). Maybe there isn't a difference, I don't know, but I've seen both used in DirectX 11 applications, and I thought maybe people who were more keen to DirectX 10 would use the effect file method, because when I learned a little DirectX 10 before I moved to DirectX 11, I used that method.

For examples of where I've seen these two methods, I learned most of what I know from these two sites:

http://rastertek.com/tutdx11.html

http://www.braynzarsoft.net/index.php?p=DX11Lessons

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

I think your overall approach is fine, assuming that everything works. I recommend ignoring your performance concerns until you can show that performance is bad (I got the impression that performance is a personal concern and not yet a technical one).

Your shader base class/interface MapData has a problem because it is an abstraction (there is no single way to map data for all shaders), and as such needs an abstract way to receive parameters. Here is one idea to consider:

Use an abstract parameter collection similar to the following:

//IShaderParameters.h
class IShaderParameters { 
    public:
        virtual ~IShaderParameters();
        TransformBuffer getTransformBuffer() const = 0;
        LightingBuffer getLightingBuffer() const = 0;  
}



//ShaderBase.h
//...
class ShaderBase {
//...
virtual void MapData(IShaderParameters* parameters);
//...
}

A default shader parameter class would extend IShaderParameters for your general purpose shaders. Specialized shaders would receive specialized forms of IShaderParameters, like so:

//WaterShaderParameters.h
//specialization extends the standard implementation
class WaterShaderParameters : public ShaderParametersBase { 
   public:
    //...
    WaterDataBuffer getWaterDataBuffer() const;
    //...
}

You'd need to guarantee that the caller knows which shader it's passing parameters to, but this is a problem that you already have.

I hope this helps. Good luck.

\$\endgroup\$
1
  • \$\begingroup\$ I never thought about it that way, that's a really good idea! Thanks :) \$\endgroup\$
    – smoth190
    Commented Feb 28, 2012 at 21:41

You must log in to answer this question.

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