Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm using a gaussian blur fragment shader. In it, I thought it would be concise to include 2 subroutines: one for selecting the horizontal texture coordinate offsets, and another for the vertical texture coordinate offsets. This way, I just have one gaussian blur shader to manage.

Here is the code for my shader. The {{NAME}} bits are template placeholders that I substitute in at shader compile time:

#version 420

subroutine vec2 sample_coord_type(int i);
subroutine uniform sample_coord_type sample_coord;

in vec2 texcoord;
out vec3 color;

uniform sampler2D tex;
uniform int texture_size;

const float offsets[{{NUM_SAMPLES}}] = float[]({{SAMPLE_OFFSETS}});
const float weights[{{NUM_SAMPLES}}] = float[]({{SAMPLE_WEIGHTS}});


subroutine(sample_coord_type) vec2 vertical_coord(int i) {
    return vec2(0.0, offsets[i] / texture_size);
}

subroutine(sample_coord_type) vec2 horizontal_coord(int i) {
    //return vec2(offsets[i] / texture_size, 0.0);
    return vec2(0.0, 0.0); // just for testing if this subroutine gets used
}

void main(void) {    
    color = vec3(0.0);

    for (int i=0; i<{{NUM_SAMPLES}}; i++) {
        color += texture(tex, texcoord + sample_coord(i)).rgb * weights[i];
        color += texture(tex, texcoord - sample_coord(i)).rgb * weights[i];
    }
}

Here is my code for selecting the subroutine:

blur_program->start();
blur_program->set_subroutine("sample_coord", "vertical_coord", GL_FRAGMENT_SHADER);
blur_program->set_int("texture_size", width);
blur_program->set_texture("tex", *deferred_output);
blur_program->draw(); // draws a quad for the fragment shader to run on

and:

void ShaderProgram::set_subroutine(constr name, constr routine, GLenum target) {
    GLuint routine_index = glGetSubroutineIndex(id, target, routine.c_str());
    GLuint uniform_index = glGetSubroutineUniformLocation(id, target, name.c_str());
    glUniformSubroutinesuiv(target, 1, &routine_index);

    // debugging
    int num_subs;
    glGetActiveSubroutineUniformiv(id, target, uniform_index, GL_NUM_COMPATIBLE_SUBROUTINES, &num_subs);
    std::cout << uniform_index << " " << routine_index << " " << num_subs << "\n";
}

I've checked for errors, and there are none. When I pass in vertical_coord as the routine to use, my scene is blurred vertically, as it should be. The routine_index variable is also 1 (which is weird, because vertical_coord subroutine is the first listed in the shader code...but no matter, maybe the compiler is switching things around)

However, when I pass in horizontal_coord, my scene is STILL blurred vertically, even though the value of routine_index is 0, suggesting that a different subroutine is being used. Yet the horizontal_coord subroutine explicitly does not blur.

What's more is, whichever subroutine comes first in the shader, is the subroutine that the shader uses permanently. Right now, vertical_coord comes first, so the shader blurs vertically always. If I put horizontal_coord first, the scene is unblurred, as expected, but then I cannot select the vertical_coord subroutine! :)

Also, the value of num_subs is 2, suggesting that there are 2 subroutines compatible with my sample_coord subroutine uniform.

Just to re-iterate, all of my return values are fine, and there are no glGetError() errors happening.

Any ideas?

share|improve this question
    
As a side-note, the usual way to set a blur pass as happening horizontally or vertically is simply to pass in the desired offset direction as a uniform vector or a pair of uniform floats. –  Trevor Powell Apr 9 '12 at 7:56
    
@TrevorPowell Yeah, someone on opengl.org schooled me on that. It's much simpler than this. cheers –  amoffat Apr 9 '12 at 9:13
add comment

1 Answer 1

up vote 6 down vote accepted

After a lot of pain, I figured out that I was indirectly calling glUseProgram() twice with my framework. The first time, to set the subroutine, the second time, in the draw() method of my shader program object. Apparently subroutine selection do not persist in shader program state, so it was being reset on the second call.

Hopefully this saves someone some time! :)

share|improve this answer
    
Nice observation! –  teodron Apr 9 '12 at 8:02
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.