Let's consider the consequences of this idea.
we could build a string that #defines variables for the desired locations of each semantic. For example, we could build a string that will end up inserting the following to the beginning of each of our Shaders:
Well, this is bad on two counts. First, there's the #version
issue. If you want to use any GLSL version other than 1.10, you must provide a #version
declaration. And that declaration must be the first thing in the shader, outside of comments and whitespace.
By putting these #define
s into your shader source (whether by string concatenation, or by using multiple strings as you do), then you have to accept certain consequences. Usually, each individual shader file will have its own #version
declaration, specifying what GLSL version it uses. But you can't do that if you want to use something besides GLSL 1.10. You have to have your C++ source code generate the #version
, before your #define
s.
This means that your shader source is now decoupled from the version it compiles under. Which is doable, but it means that your shader source is now unclear without knowing what version it is. You could communicate the version in some other way, such as with a filename (for example, lit_transform_330.vert
would use version 3.30). But you'll have to devise such a system.
Now that the version issue is sorted out, on to the next problem: what you are doing is redundant.
You use terms like "semantic", which have no meaning to OpenGL. It appears that you're trying to assign some form of name to a particular vertex attribute, so that you can see uses of that name in your shader and C++ code, and therefore know what attribute it is for.
That is, you want to define a mapping between "name" and "attribute index". You want it defined in one place, such that it is automagically propagated to every shader and used consistently throughout your C++ source code.
Well we already have a mapping between a name and the attribute index. It's called "the mapping between the attribute's name and the attribute's index". Every shader must provide a name for it's attributes. That's the string name you see in definitions like in vec4 position;
the attribute's name is position
. That's what GLSL calls the variable when it uses it.
As stated in the answer you linked to, you can associate a particular attribute name with an attribute index from C++ code before the program is linked. This is done via the glBindAttribLocation
function. You can set any number of mappings you like. When the program is linked, attributes that match to a specified location will be assigned that location.
All you need is a list of your "semantics" (aka: attribute indices) and the string names you require shaders to use for those attributes.
You might say, "Well, I want shaders to have the freedom to call the variable whatever the want." My response would be... what's the difference? Your suggested scheme already requires the user to adhere to a specific naming convention. It's just that the name they must use isn't the variable's name; it's the name of some tag you associate with the variable at declaration time.
So what exactly is the difference? That the writer of a shader has to adhere to a set naming scheme for vertex attribute variable name? Isn't a consistent name for the same concept across all shaders a good thing?
The one difference is that, if they mistype the "semantic" under your scheme, they get a shader compilation error (since their mistyped "semantic" name won't match any actual #define
s). Whereas if they mistype the name of an attribute, they will only get a compiler error if they don't mistype that name when they use the attribute.
There are ways to catch that. It requires using program introspection to walk the list of active attributes and checking them against the expect names of attributes.
You can boil this down to a very simple set of conventions. Using your "semantic" definition:
enum VertexElementSemantic
{
POSITION, NORMAL, AMBIENT, DIFFUSE, SPECULAR,
TEX_COORD0, TEX_COORD1, TEX_COORD2, TEX_COORD3,
INDICES, NUM_SEMANTICS
};
const char *AttributeNames[] =
{
"position", "normal", "ambient", "diffuse", "specular",
"tex_coord0", "tex_coord1", "tex_coord2", "tex_coord3",
"indices",
}
static_assert(ARRAY_COUNT(AttributeNames) == NUM_SEMANTICS);
GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader)
{
GLuint prog = glCreateProgram();
for(int attrib = 0; attrib < NUM_SEMANTICS; ++attrib)
{
glBindAttribLocation(prog, attrib, AttributeNames[attrib]);
}
glLinkProgram(prog);
return prog;
}
Personally speaking, I would just use a number. No matter what you use, the person writing the shader is going to have to adhere to some convention. Which means when they go to write a vertex shader that takes a position, they're going to have to look up how to say "this is a position". So they're going to have to look something up in a table somewhere no matter what.
At which point, it comes down to what the most likely problem is. The most likely issues would be either someone who thinks they know the answer but is actually wrong (ie: didn't look it up), and someone who mistyped the answer. It's really hard to mistype a number (though it certainly can happen), while it's much easier to mistype POSITION_LOCATION
. The former problem could happen to either one in more or less equal numbers.
So it seems to me that you're more likely to get fewer convention mis-match problems if your convention is based on numbers rather than words.