Reputation:
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:
hardcoded to the FragColor with blur it looks like this:
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:
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
Reputation: 43319
I can, however, immediately spot a work-around for it that will probably also improve performance in the case when blur
is false.
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;
}
Dividing by MAX_SAMPLES
seems wrong to me - you actually sample your texture MAX_SAMPLES + 1
many times.
MAX_SAMPLES
, but it starts its loop at index 1 not 0.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.
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.
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.
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