Program sturcture for shaders, objects, etc

Hi,
I’m interested in how poeple structure their shader code. It’s probably easiest if I explain the simplistic solution I have just now.

A Model is a list of Parts, one for each Material (to reduce the number of texture binds).
A Part has a VertexArray (for the vertex data) and a Material (for the texture and shader info).

So to draw a Model the psuedo-code is


Foreach part Do
  part.material.beginUsing()    // glUseProgram, glBindTexture etc.
  part.vertexArray.beginUsing() // glBindBuffer, glEnableClientState, glVertexPointer etc.
  glDrawArrays(GL_TRIANGLES, 0, part.numFaces*3)
  part.vertexArray.endUsing()
  part.material.endUsing()
Done

As I said, pretty simplistic. I’m now wanting to improve things and I’m looking to see what other people have done (just high level description is fine).

One problem is I’m not sure what requirements I want. Some of the things I’m thinking about are; how would I have an “effect” shader, i.e. the Model gets drawn as usual, but also use a shader to do something else, or how to deal with setting uniforms to different values for each instance of a Model.

Really just looking for some inspiration on what path to go down since I’m sure people have some nice solutions.

Thanks,
Stuart.

An even better way is to also globally bucket-sort parts by shader. Roughly:


linkedlist shaderPartsLL[num_shaders];

foreach(modelInstance m){
	if(!m->visible())continue;
	foreach(modelPart p in m){
		shaderPartsLL[p->shaderID].append(p);
	}
}

for(int i=0;i<num_shaders;i++){
	bindShader(i);
	enableClientstates(i); // which arrays it uses
	
	foreach(modelPart p in shaderPartsLL[i]){
		setUniforms(p->uniData);
		draw(p->vbo);
	}
}

I have a couple of related questions. I admit I’m pretty new to this, so go easy if the answers are so obvious I shouldn’t have needed to ask :slight_smile:

  1. What’s the best way to apply different shaders to different parts of a scene? The only way I’ve gotten to work is to have multiple shader programs, and switch between them as the scene is drawn. This seems like it could be less than efficient, depending on the overhead involved in switching between programs.

  2. Somewhat related, with geometry shaders I need to set input & output primitive types, e.g.

glProgramParameteriEXT (pgm, GL_GEOMETRY_INPUT_TYPE_EXT, GL_LINES);
glProgramParameteriEXT (pgm, GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLES);

but for the shader to work, I need to do this before linking the shader program. That seems to mean that if I just want the geometry shader to output a bunch of triangles for one part of the scene, I have to resort to doing a glUseProgram each time through the drawing loop. Is there a better way, or am I just doing something wrong?

The only way I’ve gotten to work is to have multiple shader programs, and switch between them as the scene is drawn. This seems like it could be less than efficient, depending on the overhead involved in switching between programs.

How could there possibly be any other way to switch programs besides switching programs?

How about a single shader program that does multiple things, depending on some condition? So if for instance you’ve written a shader routine that draws hair, you’d have drawing code that says something like

if (drawing_cat)
shader does hair
else
shader does nothing

Are you assuming that the same texture won’t generally be used by different shaders? Or do you mean sort by texture and then sort each of those by shader (or vice versa)? Is glUserProgram as bad as binding a texture? Or am I missing something?

Thanks.

No, sort by texture, and then on each part set texture (can do sorting, too)
so, first sort by shader, then sort by texture.

jamesqf: 1) What’s the best way to apply different shaders to different parts of a scene? The only way I’ve gotten to work is to have multiple shader programs, and switch between them as the scene is drawn. This seems like it could be less than efficient, depending on the overhead involved in switching between programs.

large shader programs do not guarantee to run on older hardware depending on instruction number the hardware supports. but what you ment and what is best in this case is to sort all data by shaders. bind shader used by geometry, draw all geometry using that shader, bind next, draw next, …!