GLSL布尔评估wackiness

5 opengl glsl

我的片段着色器中有一个简单的开/关开关.我想比较运动模糊传递的输出和未处理的输出,所以我在片段着色器中添加了一个布尔均匀,如下所示:

uniform boolean blur;
Run Code Online (Sandbox Code Playgroud)

我激活它(基于键盘输入和称为模糊的全局int),如下所示:

if ( key == 'b' ) blur = !blur;
glUseProgram( program );
glUniform1i( blur_loc, blur );
Run Code Online (Sandbox Code Playgroud)

然后在着色器中声明我所有的其他制服,如下所示:

uniform bool blur;
Run Code Online (Sandbox Code Playgroud)

并阅读如下:

if ( blur ) {
    FragColor = texture( color_texture, uv );
} else {
    FragColor = color/MAX_SAMPLES;
}
Run Code Online (Sandbox Code Playgroud)

现在这里是有趣的部分......当我删除if子句时,这两个FragColor语句中的任何一个都可以正常工作.硬编码到FragColor而没有模糊,它看起来像这样:

noblur

用模糊硬编码到FragColor它看起来像这样:

模糊

buuuut ...一旦我添加了if子句,就会呈现第三个(!)意外图像...看起来过于夸张,或添加剂混合或其他东西......像这样:

WTF

究竟发生了什么?如何添加一个布尔if子句,导致呈现一个完整的不同图像?我唯一可以提出的是存在某种内存损坏或者着色器由于某种原因运行两次(看起来好像我添加了两个图像)...可能导致这种情况,我使用的是什么统一布尔像个傻瓜?

UPDATE

似乎在if子句中,非模糊片段写入作为一种附加物,模糊片段写入正常...无论我把它们放入什么顺序.什么给出了什么?

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

要么

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

再次,如果我摆脱if子句并只使用其他一个陈述,他们正确地写出非模糊图像或模糊图像......

FragColor = color/MAX_SAMPLES; // correctly writes a blurred image
Run Code Online (Sandbox Code Playgroud)

要么

FragColor = texture( color_texture, uv ); // correctly writes a non-blurred image
Run Code Online (Sandbox Code Playgroud)

更新2

所以我发现了一些文档,当在条件语句中完成时,状态纹理采样是未定义的...(下面的评论之一也暗示了这一点).但是,即使纹理采样发生在if语句之外,行为也是一样的:

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
}
Run Code Online (Sandbox Code Playgroud)

更新3

在keltar的评论让我在屏幕上绘制模糊的值以确保它会将屏幕全部变为黑色或全白(它确实)之后,我决定尝试一个非常基本的if-then然后写一个蓝色片段或者红色片段取决于我传入的布尔值:

if ( blur ) {
   FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
   FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );
}
Run Code Online (Sandbox Code Playgroud)

当然,这很有用.这里是有趣的地方......

if ( blur ) {
    FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
    FragColor = color/MAX_SAMPLES;
}
Run Code Online (Sandbox Code Playgroud)

也有效......也是如此

if ( blur ) {
    FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
} else {
    FragColor = texture( color_texture, uv );
}
Run Code Online (Sandbox Code Playgroud)

只有当我感兴趣的两件事都涉及时:(

if ( blur ) {
    FragColor = texture( color_texture, uv ):
} else {
   FragColor = color/MAX_SAMPLES;
}
Run Code Online (Sandbox Code Playgroud)

也许值得注意的是变量也color开始作为纹理样本的生命,实际上是同一个:

vec4 color = texture( color_texture, uv );
Run Code Online (Sandbox Code Playgroud)

情节变厚了?

更新4

这是完整的着色器,(基于GPU GEMS 3的第27章)

#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 );
    }
Run Code Online (Sandbox Code Playgroud)

And*_*man 3

我看不出你的着色器有什么问题会导致这个......

然而,我可以立即找到一个解决方法,在 为false 的blur情况下,它也可能会提高性能。

我建议以这种方式重写着色器的末尾:

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;
}
Run Code Online (Sandbox Code Playgroud)

这里有一些有趣的事情需要注意:

  1. 除以MAX_SAMPLES对我来说似乎是错误的 - 实际上你对纹理进行了MAX_SAMPLES + 1多次采样。

    • 您链接到的文章除以MAX_SAMPLES它从索引1而不是0开始循环。

  2. 在更改此设置之前,您在无缘无故为假的情况下对纹理时间进行了采样,然后MAX_SAMPLES实际执行了与分支中相同的提取。blurcolor

    • 总共无缘无故地进行了11 次纹理获取(其中 10 次是间接的——动态纹理坐标)。

在不模糊的情况下,避免第 2点应该可以提高性能。因此,即使这不能解释您的根本问题,它仍然是您应该考虑的更改。

在检查你的着色器后我注意到其他一些有趣但基本上不相关的事情:

  1. 我假设这些PV矩阵实际上是你的投影和视图矩阵的逆矩阵?否则这一切都没有多大意义。我会考虑为制服使用一个名称,以表明它们是倒置的,因为对我来说P[rojection]意味着一个从视图空间转换到剪辑空间的矩阵,而不是相反。

  2. 您链接到的文章在 NDC 空间中进行速度计算。这意味着你在事情上走错了方向......你会想要重新预测世界位置。我相信如果您使用透视投影,这可能是一个问题,如果使用世界空间完成,模糊将与文章不一致(因为透视缩放不考虑到该坐标空间)。