Jump to content
  • Advertisement
Sign in to follow this  
DanielH

Select shaders based on properties

This topic is 4282 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

Hello! I'm working on a little game of mine and I've come to design the material/shader part of the rendering subsystem. Ive seen a lot of threads about this specific topic and I have a little idea how I should do it. My thought is to assign "properties" to shader programs, like "diffuse", "bumpy", "glossy" etc. A shader can have several properties, so a single shader program can be diffuse, bumpy and glossy at once. The process do define a material should be something like this: MaterialDescrion matDesc; matDesc.AddProperty("diffuse"); matDesc.AddProperty("bumpy"); // etc matDesc.SetPropertyValue("diffuse.texture", "MyTexture.dds"); matDesc.SetPropertyValue("diffuse.diffuseColor", ColorRGBA(...)); matDesc.SetPropertyValue("bumpy.normalMap", "normalmap.dds"); // etc MaterialPtr material = Material::CreateFromDescription(matDesc); The problem I'm unsure how to solve is the actual composing algorithm in "CreateFromDescription". How exacly should the shaders be selected so that the least number of passes are required? So if a material is { "diffuse", "bumpy", "glossy" } and there is in the shader library shaders with the tags: { "diffuse" } { "bumpy" } { "glossy" } { "diffuse", "glossy" } { "diffuse", "bumpy" } { "diffuse", "bumpy", "glossy", "glowing" } ...the best fit would be to do a eighter a { "diffuse", "bumpy" } + { "glossy" ] or { "diffuse", "glossy" } + { "bumpy" }. But how the heck should it be choosen algorithmically? If the hardware doesn't support {"diffuse, "glossy" }, the function will have to make a pass for each property. Anyone know some good algo? :) Also some thought of this system would be appreciated! Is it dynamic enought? What it will do is that it will assemble an material with n passes and assign render states and shaders to each pass. The renderer also supports composing a material manually so it's absoluly possible to make custom effects with custom shader programs.

Share this post


Link to post
Share on other sites
Advertisement
My first guess is that this is NP-hard or O(e^n). It just feels like a hard problem.

If you want to hope that this particular search problem is easier, your problem would be described formally as searching through partially ordered (or weakly ordered) collections for a best fit.

Share this post


Link to post
Share on other sites
I'd personally have each function (diffuse, bumpy, glossy) as a separate shader, eg. (in GLSL)

uniform sampler2D normalMap;
vec4 bumpy(vec4 inColour)
{
//code here
return newColour;
}



Then based on your material, dynamically create a main shader that calls all the functions in-turn:

void main()
{
vec4 colour = vec4(1.0);
colour = diffuse(colour);
colour = bumpy(colour);
colour = glossy(colour);
gl_FragColor = colour;
}



There are problems to this, like should the normal glossy uses be linked to the normal bumpy gets from the normal map. I'd suggest having a file that describe each function, and it's relation to other functions (eg, must come after diffuse).

You might also want to define some variables in the main shader that are available to all the functions (position, normal, etc..)

Regards
elFarto

[Edited by - elFarto on January 26, 2007 11:49:34 AM]

Share this post


Link to post
Share on other sites
I don't make an attempt at algorithmically matching which components are being used on a per-material basis - I explicitly select them.

My shaders are composed of a number of fragments, each responsible for a specific piece of data.

One part of the vertex shader produces a "localPosition" vector. I've got a version that just copies the position from the vertex stream (for static objects), a version that does skinning, a version that does morphing, and a version that does skinning and morphing.

Further down the pipeline, I've got a fragment slot that's responsible for taking "localPosition" and producing "worldPosition". I've got one that multiplies localPosition by a matrix stored in the constant table, and one that multiplies localPosition by a matrix stored in a vertex stream.

Combine those together, and you've got fully orthogonal support for any type of geometry (static, skinned, morphed, skinned & morphed, whatever) either as a single instance, or using instancing data.

There are obviously more stages than mentioned here (right now I have 6 for vertex shaders, and 9 for pixel shaders), but the basic idea is pretty useful.

Share this post


Link to post
Share on other sites
I was recently working on some a system similar to this, but I have taken a break to work on some other things while I develop the idea a little more. However, what I had come up with was to use a bit field (an 32bit unsigned integer, in my prototypes) to flag abilities that the shader contained (shader, not program). Then I would have a main shader for the program that flagged the shader modules that it made calls to, and when I created the shader program from the main shader module it would pull the shader modules with the best fit flags required by the main shader and link them to create the program. This wasn't exactly perfect and needed a little more thought, which is what I've taken a break to do.

Here is an example of what I'm talking about

const uint GLSLV_STANDARD_TRANSFORM = 0x00000001;
const uint GLSLV_TEXTURED = 0x00000002;
const uint GLSLV_VERTEX_LIGHTING = 0x00000004;
const uint GLSLV_FRAGMENT_LIGHTING = 0x00000008;

const uint GLSLF_TEXTURED = 0x00000001;
const uint GLSLF_FRAGMENT_LIGHTING = 0x00000002;

struct Shader
{
Shader_Type Type;
uint Flags;
};

struct Shader_Main
{
uint Vertex_Flags;
uint Fragment_Flags;
};

struct Program
{
Shader *Linked_Shader_Array;
Shader_Main Main;
};





An example of a shader:


vec4 light_vertex(...)
{
...
return color;
}



Your main shader:


void main(...)
{
vec4 lighted_color = light_vertex(...);
}



The above example is by no means complete, but should give you an idea. The shaders and programs can easily be formed into self managing objects. Then you can simply feed the program object the main shader and it can select from a list of shaders the best fit to link based upon their flags. Thus allowing you to dynamically link shaders at runtime.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!