user755921
user755921

Reputation:

GLSL boolean evaluation wackiness

I am having quite a time with a simple on/off switch in my fragment shader. I want to compare the output of a motion-blur pass with the unprocessed output, so I have added a boolean uniform to the fragment shader like such:

uniform boolean blur;

I activate it (based on keyboard input and a global int called blur) like such:

if ( key == 'b' ) blur = !blur;
glUseProgram( program );
glUniform1i( blur_loc, blur );

and then in the shader it is declared with all my other uniforms like this:

uniform bool blur;

and read like this:

if ( blur ) {
    FragColor = texture( color_texture, uv );
} else {
    FragColor = color/MAX_SAMPLES;
}

Now here is the interesting part... either of those two FragColor statements works fine on its own, when I remove the if-clause. hardcoded to the FragColor without blur it looks like this:

noblur

hardcoded to the FragColor with blur it looks like this:

blur

buuuut... once I add the if-clause a third (!) unexpected image is rendered... what looks to be overblown, or additive blended or something... like this:

wtf

What on earth is happening? how is adding a boolean if-clause causing a whole different image to be rendered? the only thing I can posit is that there is some sort of memory corruption going on OR the shader is running twice for some reason (it sort of looks like if I added both images)... what could be causing this, am I using uniform bool like a fool?

UPDATE

It seems that when in the if-clause, the non-blurred fragment write acts as an additive and the blurred fragment write acts normally... no matter what order I put them in. What gives?

if ( blur ) {
    FragColor = texture( color_texture, uv ); // writes an odd compound image
} else {
    FragColor = color/MAX_SAMPLES; // correctly writes a blurred image
}

or

if ( blur ) {
    FragColor = color/MAX_SAMPLES; // correctly writes a blurred image
} else {
    FragColor = texture( color_texture, uv ); // writes an odd compound image
}

again, if I get rid of the if-clause and use just one of the other statements, they correctly write either the non-blurred image or the blurred image...

FragColor = color/MAX_SAMPLES; // correctly writes a blurred image

or

FragColor = texture( color_texture, uv ); // correctly writes a non-blurred image

UPDATE 2

So I have found some docs that state texture sampling is undefined when done in a conditional statement... (one of the comments below also suggested this). However, the behavior is the same even when the texture sampling happens outside the if statement:

vec4 sampled_color = texture( color_texture, uv );
if ( blur ) {
    FragColor = color/MAX_SAMPLES; // correctly writes a blurred image
} else {
    FragColor = sampled_color; // writes an odd compound image
}

UPDATE 3

After keltar's comment made me draw the value of blur to the screen to make sure it would turn the screen all black or all white (it did), I decided to try a very basic if-then that would either write a blue fragment or a red fragment depending on my passed-in-boolean value:

if ( blur ) {
   FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
   FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );
}

of course, this worked. here is where it gets interesting though...

if ( blur ) {
    FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
    FragColor = color/MAX_SAMPLES;
}

also works... as does

if ( blur ) {
    FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
    FragColor = texture( color_texture, uv );
}

It is only when the two things I am interested in are both involved :(

if ( blur ) {
    FragColor = texture( color_texture, uv ):
} else {
   FragColor = color/MAX_SAMPLES;
}

Perhaps it is worth noting that the variable color begins its life as a texture sample as well, in fact the very same one:

vec4 color = texture( color_texture, uv );

The plot thickens?

UPDATE 4

here is the full shader, (based on chapter 27 of GPU GEMS 3)

#version 150
in vec2 vUV;
out vec4 FragColor;
uniform sampler2D depth_tiu;
uniform sampler2D color_tiu;
uniform mat4 P;
uniform mat4 V;
uniform mat4 V_prev;
uniform bool blur;
void main() {
    float z_over_w = texture( depth_tiu, vUV ).r;
    vec4 view_pos = vec4( 2*vUV.x - 1, 2*(1 - vUV.y) - 1, z_over_w, 1.0 );
    vec4 D = P * V * view_pos;
    vec4 world_pos = D/D.w;
    vec4 previous_world_pos = P * V_prev * view_pos;
    previous_world_pos /= previous_world_pos.w;
    vec2 velocity = vec2((world_pos - previous_world_pos)/2.0);
    velocity = clamp( velocity, vec2(-0.001, -0.001), vec2(0.001, 0.001) );
    int MAX_SAMPLES = 10;
    vec4 color = texture( color_tiu, vUV );
    vec2 write_UV = vUV;
    write_UV += velocity;
    for ( int i = 0; i < MAX_SAMPLES; ++i, write_UV += velocity ) {
        vec4 curr_color = texture( color_tiu, write_UV );
        color += curr_color;
    }
    if ( blur ) {
        FragColor = color/MAX_SAMPLES;            
    } else {
        FragColor = texture( color_tiu, vUV );
    }

Upvotes: 5

Views: 3627

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43319

I cannot see anything immediately wrong with your shader that would cause this...

I can, however, immediately spot a work-around for it that will probably also improve performance in the case when blur is false.

I would suggest re-writing the end of your shader this way:

FragColor = texture( color_tiu, vUV );

if ( blur ) {
    const int MAX_SAMPLES = 10;

    vec2 write_UV = vUV + velocity;

    for ( int i = 0; i < MAX_SAMPLES; ++i, write_UV += velocity ) {
        FragColor += texture( color_tiu, write_UV );
    }

    FragColor /= MAX_SAMPLES;
}

There are a couple of interesting things to note here:

  1. Dividing by MAX_SAMPLES seems wrong to me - you actually sample your texture MAX_SAMPLES + 1 many times.

    • The article you linked to divides by MAX_SAMPLES, but it starts its loop at index 1 not 0.

  2. Prior to changing this you sampled your texture MAX_SAMPLES times in the case where blur was false for no reason and then actually performed the same fetch as color in the branch.

    • Altogether that was 11 texture fetches (10 of which were indirect -- dynamic texture coordinates) for no reason.

Avoiding point #2 should improve performance in the case where you are not blurring. Thus, even if this does not explain your underlying problem, it is still a change you should consider.

Some other interesting, but largely unrelated things I noticed after examining your shader:

  1. I assume these P and V matrices are actually inverses of your projection and view matrices? Otherwise none of this makes much sense. I would consider using a name for the uniforms that makes it obvious they are inverted, because to me P[rojection] means a matrix that transforms from view-space to clip-space rather than the other way around.

  2. The article you linked to does the velocity computation in NDC space. That means you have gone the wrong direction with things... you would want to re-project the world position. I believe this could be an issue if you are using perspective projection, the blur would not be consistent with the article if done using world-space (because perspective scaling is not factored into that coordinate space).

Upvotes: 3

Related Questions