2
votes

I'm working on a program using GLSL shaders. I coded 2 different ways to compute ADS (Ambient + Diffuse + Specular) shading in 2 different methods. To do the job properly I used subroutines to use one or the other method to compute ADS shading.

Here's a piece of the fragment shader code :

subroutine vec3 LightShadingEffectType(int idx, vec3 normal, vec3 lightDir);
subroutine uniform LightShadingEffectType LightShadingEffect;

subroutine (LightShadingEffectType)
vec3 Basic_ADS_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 reflectDir = reflect(-lightDir, normal);
    vec3 viewDir = normalize(-Position.xyz);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

subroutine (LightShadingEffectType)
vec3 Phong_ADS_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 v = normalize(vec3(-Position));
    vec3 h = normalize(v + lightDir);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(h, normal), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

And the C++ code :

type::uint32 basic_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Basic_ADS_Shading");
type::uint32 phong_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Phong_ADS_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &phong_ads_idx);

Until here the display is correct. In this case I've choosen to execute the second subroutine definition (Phong_ADS_Shading call).

But I want to declare another subroutine type in my program to manage textures (the signature is not the same). Here's an other piece of the fragment shader code (in the same shader) :

subroutine vec4 TexturedShadingType();
subroutine uniform TexturedShadingType TexturedShading;

subroutine (TexturedShadingType)
vec4 Textured_Shading(void)
{
    vec4 TexColor = texture(Tex1, TexCoords);
    return (vec4(getLightIntensity(), 1.0f) * TexColor);
}

subroutine (TexturedShadingType)
vec4 Untextured_Shading(void)
{
    return (vec4(getLightIntensity(), 1.0f));
}

So, finally, my C++ code is the following :

type::uint32 basic_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Basic_ADS_Shading");
type::uint32 phong_ads_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Phong_ADS_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &phong_ads_idx);

type::uint32 textured_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Textured_Shading");
type::uint32 untextured_idx = glGetSubroutineIndex(program->getHandle(), GL_FRAGMENT_SHADER, "Untextured_Shading");

glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &untextured_idx);

Here's the value for each subroutine index :

std::cout << phong_idx << ", " << blinn_phong_idx << ", " << textured_idx << ", " << untextured_idx << std::endl;

-> 1, 0, 4294967295, 4294967295

The two first values seems to be correct but the two others.

Here's now the whole fragment shader code to have a better understanding of my problem:

#version 400

in vec3 Position;
in vec3 Normal;
in vec2 TexCoords;

layout (location = 0) out vec4 FragColor;

uniform sampler2D Tex1;
uniform int lightCount;

struct PointLight
{
    vec4 Position;
    vec3 La, Ld, Ls;
    float Kc, Kl, Kq;
    vec3 direction;
    float exponent;
    float cutoff;
    int type;
};

struct Material
{
    vec3 Ka, Kd, Ks, Ke;
    float Shininess;
};

uniform PointLight LightInfos[10];
uniform Material MaterialInfos;

subroutine vec3 LightShadingEffectType(int idx, vec3 normal, vec3 lightDir);
subroutine uniform LightShadingEffectType LightShadingEffect;

subroutine (LightShadingEffectType)
vec3 Phong_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 reflectDir = reflect(-lightDir, normal);
    vec3 viewDir = normalize(-Position.xyz);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

subroutine (LightShadingEffectType)
vec3 Blinn_Phong_Shading(int idx, vec3 normal, vec3 lightDir)
{
    vec3 v = normalize(vec3(-Position));
    vec3 h = normalize(v + lightDir);

    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;
    vec3 Diffuse = LightInfos[idx].Ld * MaterialInfos.Kd * max(dot(lightDir, normal), 0.0f);
    vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(h, normal), 0.0f), MaterialInfos.Shininess);
    vec3 Emissive = MaterialInfos.Ke;

    return (Ambient + Diffuse + Specular + Emissive);
}

float getLightAttenuation(vec3 lightDir, PointLight light)
{
    float lightAtt = 0.0f;
    float dist = 0.0f;

    dist = length(lightDir);
    lightAtt = 1.0f / (light.Kc + (light.Kl * dist) + (light.Kq * pow(dist, 2)));
    return (lightAtt);
}

float getSpotFactor(vec3 lightDir, vec3 spotDir, PointLight light)
{
    return (pow(dot(-lightDir, spotDir), light.exponent));
}

vec3 Spot_ADS_Shading(float lightAtt, vec3 tnorm, vec3 lightDirNorm, int idx)
{
    vec3 LightIntensity = vec3(0.0f);
    vec3 spotDirNorm = normalize(LightInfos[idx].direction);
    float angle = acos(dot(-lightDirNorm, spotDirNorm));
    float cutoff = radians(clamp(LightInfos[idx].cutoff, 0.0f, 90.0f));

    if (angle < cutoff)
    {
        float spotFactor = getSpotFactor(lightDirNorm, spotDirNorm, LightInfos[idx]);
        LightIntensity = lightAtt * spotFactor * LightShadingEffect(idx, -tnorm, lightDirNorm);
    }
    else
    {
        LightIntensity = LightShadingEffect(idx, -tnorm, lightDirNorm) * MaterialInfos.Ka;
    }
    return (LightIntensity);
}

vec3 Point_ADS_Shading(float lightAtt, vec3 tnorm, vec3 lightDirNorm, int idx)
{
    return (lightAtt * LightShadingEffect(idx, tnorm, lightDirNorm));
}

vec3 getLightIntensity(void)
{
    vec3 LightIntensity = vec3(0.0f);

    for (int idx = 0; idx < lightCount; idx++)
    {
        vec3 tnorm = (gl_FrontFacing ? -normalize(Normal) : normalize(Normal));
        vec3 lightDir = vec3(LightInfos[idx].Position) - Position;
        vec3 lightDirNorm = normalize(lightDir);
        float lightAtt = getLightAttenuation(lightDir, LightInfos[idx]);

        if (LightInfos[idx].type == 1)
        {
            LightIntensity += Spot_ADS_Shading(lightAtt, tnorm, lightDirNorm, idx);
        }
        else
        {
            LightIntensity += Point_ADS_Shading(lightAtt, -tnorm, lightDirNorm, idx);
        }
    }
    return (LightIntensity);
}

subroutine vec4 TexturedShadingType();
subroutine uniform TexturedShadingType TexturedShading;

subroutine (TexturedShadingType)
vec4 Textured_Shading(void)
{
    vec4 TexColor = texture(Tex1, TexCoords);
    return (vec4(getLightIntensity(), 1.0f) * TexColor);
}

subroutine (TexturedShadingType)
vec4 Untextured_Shading(void)
{
    return (vec4(getLightIntensity(), 1.0f));
}

void main(void)
{
    FragColor = TexturedShading();
}

The problem is my geometry is render in black. I think there is a conflict between the two uniform subroutines. I'm lost. Does anyone can help me ?

1

1 Answers

6
votes

glUniformSubroutines sets all of the subroutines for a shader stage, not just one of them.

See, when OpenGL links your program, it takes all of the subroutine uniforms and builds an array out of them. Each uniform has an index into this array. If you want to figure out what the index for a particular subroutine uniform in the array is, you need to call glGetSubroutineIndex. Alternatively, assuming you have 4.3/ARB_explicit_uniform_locations (which admittedly AMD is rather slow on), you can just set this directly with the layout(location = #) layout qualifier. That way, you don't have to query it.

Once you know what index each subroutine uniform refers to, you can then set all of the subroutine uniforms for a stage with a single call to glUniformSubroutines. You build up a short array, where each index in the array contains the index of the subroutine function you want to use.


But I want to choose just 2 subroutines each time among the four subroutines.

You may have 4 subroutines, but you only have two subroutine uniform variables. These uniforms represent the user setting the particular function in the shader to be called.

Furthermore, both of the uniforms use different types, so they cannot select from among the 4 subroutines. Each subroutine uniform can only select from among the specific subroutines used by that particular type. This is defined when you declare the subroutine function with subroutine(SubroutineType). Each type has its own set of functions which it could be used with. So each uniform can only select from among those specific functions set in the subroutine type declared for that uniform.

So you can't choose among 4 subroutines; each uniform can only choose among the functions you set for each subroutine type. Each uniform can only pick between the two functions you declared the subroutine type with.

If I have just a single call of glUniformSubroutinesuiv how can I select them ?

By passing it an array. There's a reason why the third parameter is a pointer, and why the second parameter is the number of entries in the array.

Your fragment shader has two subroutine uniform values. Therefore, there are 2 elements in the array, each one of which represents the subroutine index for a particular subroutine uniform. You select them both by creating an array, setting both subroutine indexes into it, and passing that array to the function.

I think there is a conflict between the two uniform subroutines.

No, the problem (besides the array issue noted earlier) is that you're not using the other subroutine. While subroutine uniforms are different from regular uniforms in most respects, they are like regular uniforms in this way. If you don't use them, the driver can optimize them (and anything they rely on) away.

Your fragment shader probably doesn't use TexturedShading anywhere. And since there's no other subroutine uniform that declares a TexturedShadingType uniform, the compiler realizes that those two functions are never used as subroutines. So it optimizes them out. Therefore, the indices you get for them are GL_INVALID_INDEX.