Handling Shader Name Collisions

What’s the best way to handle name collisions with an ubershader? For example, sometimes the ubershader will use a geometry shader. When it uses a geometry shader, fragment shader varying names have to change.

For example, normal path:

Vertex shader: float foo;
Fragment shader: float foo;

When the geometry shader is in the mix, “Foo” needs to go through all three shaders and avoid naming collisions.

Vertex shader: varying out float vFoo;
Geometry shader: varying in float vFoo;
varying out float gFoo;
Fragment shader: varying in float gFoo;

So, the problem is that the fragment shader varying names have to change depending on whether a geometry shader is used or not.

I used fragment shader code fragments to handle this situation, so that I could update only parts of the fragment shader instead of maintaining two complete versions of it. Basically I have two separate versions to query varyings:


// vertex only varying fetch
varying in float vFoo;
float getFoo() { return vFoo; }

or


// geometry shader varying fetch
varying in float gFoo;
float getFoo() { return gFoo; }

Then you can link or concatenate this code into your main fragment shader and call getFoo() to access your varying. This can be extended to any number of varyings using the two code blocks.

If anyone has a more efficient way of doing this, I’d be interested too.

When it uses a geometry shader, fragment shader varying names have to change.

No, they don’t. The inputs and outputs of a geometry shader can be contained in input/output interface blocks, which can be given a scoped name. This way, you can use the same name for the input and output names, while having a way to distinguish them in the geometry shader.

Vertex shader:


out FSOut
{
  vec4 color;
  vec2 texCoord;
  vec3 normal;
} vsOut;

void main()
{
  vsOut.color = ...;
  vsOut.normal = ...;
  vsOut.texCoord = ...;
  gl_Position = ...
}

Geometry shader:


in FSOut
{
  vec4 color;
  vec2 texCoord;
  vec3 normal;
} vsOut[];

out FSOut
{
  vec4 color;
  vec2 texCoord;
  vec3 normal;
} toFS;

void main()
{
  for(vertex count)
  {
    toFS.color = ...;
    toFS.texCoord = ...;
    toFS.normal = ...;
    EmitVertex();
    ...
  }
}

Fragment shader


in FSOut
{
  vec4 color;
  vec2 texCoord2;
  vec3 theNormal;
} vsOut;

out vec4 color;

void main()
{
  color = ...
}

Notice several things. First, the use of the input/output blocks between the vertex and fragment shaders. I renamed some of the variables in the FS block; this is perfectly fine, because GLSL matches input/output blocks by the block name.

Also note that the GS has two blocks with the same name. This is also fine, because the blocks are declared as input and output. They have different namespaces.

Thanks. I’ll have to give that a shot.

Ah, that’s much nicer. [pining for the day when Apple updates their GLSL version to support it]

Thanks, Alphonse, that’s a nice feature!