Sign in to follow this  

material system

This topic is 2037 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I'm trying to create a material system for my deferred renderer also I'm using effects framework with dx 11. Currently I don't have material class and I have a class for every shader that I'm using and I'm setting shader parameters from that class but obviously it is not a good approach and I was using it just for the beginning. So before go any further I want to change it. And I'm trying to find what my material and shader classes should include and how they use that information. For example material obviously should know about material related variables in shader but there are other variables that are not related to how things are seen like matrices so which part of the game should be responsible for setting these kind of variables. And I'm thinking about writing xml for every shader which includes details about variables in the corresponding shader so I can use one universal shader class which has list of shader variables created using the information in xml file.

Share this post


Link to post
Share on other sites
[quote name='ekba89' timestamp='1336700438' post='4939182']For example material obviously should know about material related variables in shader but there are other variables that are not related to how things are seen like matrices so which part of the game should be responsible for setting these kind of variables.[/quote]Matrices [b]are [/b]material parameters. There have been a few discussions in the past about that and the hint was about considering parameters as opaque blobs. I cannot quite point you to the exact thread, hopefully someone will search for it.

I'd elaborate a bit on that concept though. Some special values must obviously have a special binding. I think D3D somehow has this built in (I think it's called DXSAS but never looked at it). In general, I considered my parameters a sequence of opaque blobs with an header. If the header mandated the use of an [i]environment value[/i] - such as MVP or light info - then the blob would count 0 bytes and I would pull out the value from runtime.

Who does the pulling? Someone very close to the renderer, but not quite like it. I've called my pullers [i]uniform fetchers[/i].

Share this post


Link to post
Share on other sites
Just to give you some input, here's my Material class (stripped of its member functions):

[CODE]class Material
{
public:

enum CullFace
{
DONT_CULL,

CULL_FRONT,
CULL_BACK
};

enum DepthTest
{
DONT_TEST,

DEPTH_TEST_NEVER,
DEPTH_TEST_ALWAYS,

DEPTH_TEST_EQUAL,
DEPTH_TEST_NOT_EQUAL,

DEPTH_TEST_LESS,
DEPTH_TEST_LESS_EQUAL,

DEPTH_TEST_GREATER,
DEPTH_TEST_GREATER_EQUAL
};

enum BlendMode
{
DONT_BLEND,

BLEND_MODULATE,
BLEND_ADDITIVE,
BLEND_TRANSPARENT
};

vec4 ambient;
vec4 diffuse;
vec4 specular;

bool colorWrite;
bool depthWrite;

CullFace cullFace;
DepthTest depthTest;
BlendMode blendMode;

protected:

// contains shader program id, etc.
shared_ptr<MaterialProxy> m_proxy;

// texture layers contain textures and texture coordinate
// generators used to scroll, stretch and rotate textures
vector<shared_ptr<TextureLayer>> m_textureLayers;
};[/CODE]

I use a uniform buffer object to update material colors etc. efficiently, I think there's a similar concept available in DX. Edited by Vexator

Share this post


Link to post
Share on other sites
[quote name='Krohm' timestamp='1336723690' post='4939231']
There have been a few discussions in the past about that and the hint was about considering parameters as opaque blobs. I cannot quite point you to the exact thread, hopefully someone will search for it.
[/quote]

[url="http://www.gamedev.net/topic/604899-frostbite-rendering-architecture-question/"]Here you go[/url]. This is most likely the more complete thread about material systems.

Share this post


Link to post
Share on other sites
No, that was not quite the post I was referring to. It's related, seems to involve the management of the various materials but it does not seem to go much in depth about what a material is.
I think [url="http://www.gamedev.net/topic/606658-should-materials-contain-corresponding-shader-programs/"]this [/url]could be slightly more in my line of thinking... but I'm not 100% sure. It seems to contain all the details.

As a side note: I would avoid the material structure above. It is adeguate for D3D7 and FFP: an extremely inflexible way to think at things now.

My best suggestion to OP is to approach this iteratively, by focusing on the needs of a particular product first. For a start, Vertex shader + pixel shader + (a subset of) ROP settings coupled with constant data will likely suffice.

Share this post


Link to post
Share on other sites
First of all thanks for the advices. And here is my strategy. First I think I should change my shader variables.
Currently they are like below and I have proper ID3DX11Variable type for each them.
[CODE]
float3 lightDirection;
float3 lightColor;
[/CODE]
And in order to use them as raw data with ID3DX11EffectConstantBuffer it should be like this
[CODE]
cbuffer LightInfo
{
float3 lightDirection;
float3 lightColor;
};
[/CODE]
And shader class should have a list of ID3DX11EffectConstantBuffer and should know what they contain. And material class should have actual buffers for these variables. I'm not sure what is the best way to make shader class know about contents of buffers though. And another thing I'm curious about is that shader variables are filled by different parts of the game. I mean view and projection matrices come from camera class, some values come from object's class etc. And what is the best approach to update material contents?
That was my strategy and my problems [img]http://public.gamedev.net//public/style_emoticons/default/biggrin.png[/img]. Is it a viable strategy or does it need to change?

Share this post


Link to post
Share on other sites
In my engine I have a Frame Constant Buffer that is only updated once per frame and containts matrices like View, Proj, ViewProj matrices and other constants that only change once per frame... This CB is accessible to all shaders in the CB slot 0. Then I have a ActorCB that contains the constants of a actor, and the number of times it is updated = number of actors drawn, and the same goes for the other Constant Buffers

Share this post


Link to post
Share on other sites
[quote name='TiagoCosta' timestamp='1336834591' post='4939573']
In my engine I have a Frame Constant Buffer that is only updated once per frame and containts matrices like View, Proj, ViewProj matrices and other constants that only change once per frame... This CB is accessible to all shaders in the CB slot 0. Then I have a ActorCB that contains the constants of a actor, and the number of times it is updated = number of actors drawn, and the same goes for the other Constant Buffers
[/quote]
Yes I was thinking about something like this but how do you know what is inside the buffers. I mean different shaders have different CBs. And some of them might not have per frame matrix data. Are you using another file that has information about CBs or is there a way to find out CB when compiling the shader.

Share this post


Link to post
Share on other sites
I have a file called ShadersHeader.hlsl that looks something like this:
[CODE]
//Samplers
SamplerState gTriLinearSampler : register(s0);
SamplerState gTriPointSampler : register(s1);
SamplerState gAnisoSampler : register(s2);
//Constant Buffers
cbuffer cbImmutable : register(b0)
{
//Constants that never change during gameplay
}
cbuffer cbPerFrame : register(b1)
{
float4x4 gView;
float4x4 gProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float4 gEyePositionFarPlane;
}
[/CODE]

Then I [i]#include[/i] this header in every shader file that needs some/all of this constants. So when I update the constant buffer at slot 0/1 the constants will be available to every shader... you just have to make sure you don't override constant buffer slots.
I'm not sure how the effects framework will deal with this, but it will probably work the same way, anyway why don't you write your own effect framework? You will gain more control over the shaders and probably a performance boost.

EDIT: In Effects11 I think you can share samplers/constant buffers, and other resources, across techniques by using Effect Groups, although I'm not sure how they work... Edited by TiagoCosta

Share this post


Link to post
Share on other sites
Thanks for the reply. I'm not against writing my own effect framework but currently I don't need something like that and before I wrote a simple effect framework so I know how it works. And about my question, I don't have problems with CBs that are used by all shaders. My question is lets say there are two shaders A and B. And they have different variables in CBs. So to create corresponding variables in shader and material class I should know about what their CBs contain. And my solution to that is having xml file for every shader which contains info about shader variables. Something like this.
[CODE]
//SHADER A
cbuffer buffer
{
float4 a;
float3 b;
};

//xml
<variable name="a" type="float4" />
<variable name="b" type="float3" />
[/CODE]

[CODE]
//SHADER B
cbuffer buffer
{
matrix a;
float b;
};

//xml
<variable name="a" type="matrix" />
<variable name="b" type="float" />
[/CODE]

And this way I can know what it contains and its size. But I'm not sure this is the easiest and the most efficient way to do this. Edited by ekba89

Share this post


Link to post
Share on other sites
You can do that, but you can also just use reflection to figure out which variables are in a constant buffer. Everything about a constant buffer can be queried with the reflection API's, you don't need your own layout scheme to find out that information.

Share this post


Link to post
Share on other sites
[quote name='MJP' timestamp='1336871047' post='4939677']
You can do that, but you can also just use reflection to figure out which variables are in a constant buffer. Everything about a constant buffer can be queried with the reflection API's, you don't need your own layout scheme to find out that information.
[/quote]
Thanks! I think thats exactly what I need. And if anyone curious I found [url="http://members.gamedev.net/JasonZ/Heiroglyph/D3D11ShaderReflection.pdf"]this[/url] about reflection. Also I think it might be possible to get these info with effects framework (I've just found that). And before I jump into coding is this a good approach to material and shader system. Because another question popped up in my head "How should I update buffers?" and I'm not sure about that :D. Because after I created buffers with the info I got from reflection (or effects framework if possible) I need to send necessary information to material class to update buffers. And obviously different materials need different information. How can I create uniform interface for sending necessary information to material class?

Share this post


Link to post
Share on other sites
I keep it simpler in my case. Since you're writing a deferred shading engine, you are kind of limited in the material types you can create anyway.

I let people specify things like diffuse map, specular and gloss map, normal map, blend mode, etc...

It's easy to work with and gives me exactly what I want. Ogre3D has a much more powerful material system from what I've seen.

Also I use a bitmask depending on features in the shader. I have a map of bitmask to shader and if that shader is already loaded and compiled I use it. The bitmask also decides which #define keywords are defined when compiling the uber shaders.

Share this post


Link to post
Share on other sites
Ok it is mostly finished. Here is how I did it. Hopefully someone having the same problems can get an idea and you can help me to improve it.

This is my shader class. Shader class keeps a list of ID3DX11EffectVariable and 2 variables for per frame and per object constant buffers so I can update them individually or all together if I want. And what I like about my shader class is it can create input layout automatically. If anyone needs that I can post it too. And I have a question about FreeTextures function. Since I'm using deferred renderer some of my textures are used as render targets so I need to free them before using as render targets. And here is how I do it but I don't know if this is a good way to do it. Because when I apply pass to device context doesn't it updates all of the variables?

[CODE]
void Shader2::FreeTextures()
{
std::map<std::string, ID3DX11EffectShaderResourceVariable*>::iterator i;
for(i = textures.begin(); i != textures.end(); i++)
{
i->second->SetResource(0);
}
ID3DX11EffectTechnique* tech = techniques[0];
tech->GetPassByIndex(0)->Apply(0, graphicsDevice->GetDeviceContext());
}
[/CODE]

[CODE]
class Shader2
{
friend class Material;
public:
struct VariableContainer
{
ID3DX11EffectVariable* variable;
UINT size;
};
Shader2(void);
~Shader2(void);
void Initialize(GraphicsDevice* gDevice, std::string path);
void Release();
void SetVariable(const std::string name, void* data);
void SetVariable(const std::string name, bool data);
void SetVariable(const std::string name, float data);
void SetVariable(const std::string name, D3DXMATRIX data);
void SetTexture(std::string name, ID3D11ShaderResourceView* texture);
void SetPerFrameConstantBuffer(void* data, UINT size);
void SetPerFrameConstantBuffer(void* data, UINT offset, UINT size);
void SetPerObjectConstantBuffer(void* data, UINT size);
void SetPerObjectConstantBuffer(void* data, UINT offset, UINT size);
void FreeTextures();
void Render();
private:
void CreateShader();
void CreateInputLayout();
private:
GraphicsDevice* graphicsDevice;
//Effect
ID3DX11Effect* effect;
//Input layout
ID3D11InputLayout* inputLayout;
/*per frame constant buffer is always on register1 and it's name is perFrameConstantBuffer
*per object constant buffer is always on register2 and it's name is perObjectConstantBuffer
*/
ID3DX11EffectConstantBuffer* perFrameConstantBuffer;
ID3DX11EffectConstantBuffer* perObjectConstantBuffer;
//individual variables with their names
std::map<std::string, VariableContainer> variables;
//texture variables with their names
std::map<std::string, ID3DX11EffectShaderResourceVariable*> textures;
//techniques
ID3DX11EffectTechnique** techniques;
UINT techniqueCount;
//file path to shader source code
std::string filePath;
};
[/CODE]

This is my material class. It keeps list of variable values and corresponding ID3DX11EffectVariables. I keep them both because it is faster this way since I have to search for the parameter name only once.Other than that nothing interesting here currently but I will add render state options, physical properties and things like that.

[CODE]
class Material
{
public:
struct VariableBuffer
{
Shader2::VariableContainer* variableContainer;
void* buffer;
};
struct TextureBuffer
{
ID3DX11EffectShaderResourceVariable* shaderResource;
ID3D11ShaderResourceView* texture;
};
Material(void);
~Material(void);
void Initialize(Shader2* shader, std::string materialName);
void Release();
void SetVariable(const std::string name, void* variable);
void SetVariable(const std::string name, float variable);
void SetVariable(const std::string name, bool variable);
void SetVariable(const std::string name, D3DXMATRIX variable);
void SetTexture(const std::string name, ID3D11ShaderResourceView* texture);
void ApplyMaterial();
private:
//shader for this material
Shader2* shader;
//material name
std::string name;
//textures and variables with their names
std::map<std::string, TextureBuffer> textures;
std::map<std::string, VariableBuffer> variables;
};
[/CODE]

Share this post


Link to post
Share on other sites

This topic is 2037 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this