Opengl着色器:如果条件错误地评估为false

时间:2016-05-27 14:53:27

标签: qt opengl glsl shader

我最近买了" opengl着色语言食谱"。 我试图编写其中一个示例:特别是反射立方体贴图。

就像现在一样,我的软件崩溃了。

我可以把这个问题缩小到一个if条件,这个条件是假的,但是" false"部分似乎已被执行(更多细节)。

目标是:

  • 画一个天空盒。
  • 画一个反映天空盒的物体(茶壶)。

开发环境: 我尝试使用QT5.6.0的2台PC(工作笔记本电脑和家用游戏机)和QT 5.3.0的笔记本电脑。 QT 5.6.0是使用mingw编译的32位版本,不确定5.3.0版本。

两台电脑都有一张nvidia卡。我不想进入细节,因为我认为问题不存在(除非在答案中另有说明)。

我已经删除了茶壶抽屉。 我目前只画Skybox。这意味着" DrawSkyBox" boolean uniform始终为true。这很重要。

顶点着色器是问题所在(或者至少是引发崩溃的那个)。 代码在这里(这是本书中的代码/项目,而不是我的,无论如何都是复制/粘贴):

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec2 VertexTexCoord;

out vec3 ReflectDir;

uniform bool DrawSkyBox;
uniform vec3 WorldCameraPosition;

uniform mat4 ModelViewMatrix;
uniform mat4 ModelMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
    if( DrawSkyBox ) {
        ReflectDir = VertexPosition;
    } else {
        vec3 worldPos = vec3( ModelMatrix * vec4(VertexPosition,1.0) );
        vec3 worldNorm = vec3(ModelMatrix * vec4(VertexNormal, 0.0));
        vec3 worldView = normalize( WorldCameraPosition - worldPos );

        ReflectDir = reflect(-worldView, worldNorm );
    }

    gl_Position = MVP * vec4(VertexPosition,1.0);
}

片段着色器在这里:

#version 430

in vec3 ReflectDir;

layout(binding=0) uniform samplerCube CubeMapTex;

uniform bool DrawSkyBox;
uniform float ReflectFactor;
uniform vec4 MaterialColor;

layout( location = 0 ) out vec4 FragColor;

void main() {
    // Access the cube map texture
    vec4 cubeMapColor = texture(CubeMapTex, ReflectDir);

    if( DrawSkyBox )
        FragColor = cubeMapColor;
    else
        FragColor = mix( MaterialColor, cubeMapColor, ReflectFactor);
}

所以我做了什么来研究这个问题:

1)在片段着色器中,我不提取立方体贴图纹理,而是设置颜色(在if / else中)。 我清楚地看到天空盒是红色的,茶壶是绿色的(在这个测试中我仍然画了两个) - >绘制调用和对象数据是正确的。

2)我尝试使用"标准" 2D纹理。我传递了标准纹理坐标,片段着色器只是获取纹理(没有if / else) - >工作得很好。

3)我发现一些驱动程序/ API可以将布尔值转换为int(这可能是旧时代的问题,但我也删除了这种可能性): 我尝试发送一个int并将条件更改为if(DrawSkyBox> 0){...}并将1传递给制服:

mProgram->setUniformValue("DrawSkyBox", 1);

即使使用类型转换:

mProgram->setUniformValue("DrawSkyBox", (int) 1);

着色器仍在执行"否则"一部分。

4)我将执行部分移动到特定的功能中,结果相同。

void CalcSkyBox()
{
    ReflectDir = VertexPosition;
}

void ObjReflect()
{
    vec3 worldPos  = vec3(ModelMatrix * vec4(VertexPosition, 1.0));
    vec3 worldNorm = vec3(ModelMatrix * vec4(VertexNormal, 0.0));
    vec3 worldView = normalize(WorldCameraPosition - worldPos);

    ReflectDir = reflect(-worldView, worldNorm );    
}

void main()
{

    if (DrawSkyBox) {
        CalcSkyBox();
    } else {
        ObjReflect();
    }

    gl_Position = MVP * vec4(VertexPosition, 1.0);
}

为什么我说它执行" else"部分从一开始?嗯...:

最后我发现通过修改" ReflectDir"在顶点着色器的else部分( - > ReflectDir = reflect(-worldView, worldNorm );替换为ReflectDir = VertexPosition;)然后它完美地工作! (我的意思是没有崩溃并显示天空盒。)

所以我重申的问题是: 为什么着色器执行else部分?

Imarion

PS:这是一个很长的阅读,所以如果你达到了这一点并得到任何答案,请提前感谢。

2 个答案:

答案 0 :(得分:0)

某些GPU可能会评估某个分支的两端,然后丢弃一个分支。请参阅:http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html

这并不能解释为什么你会崩溃。您规范化的向量是否可以为零,以便归一化导致除以零?

答案 1 :(得分:0)

我创建了2个程序来摆脱顶点着色器中的“if”。 我使用完全相同的片段着色器,它具有“if”。

然后一切顺利。

我很想解决这个问题,因为这是来自Nvidia顶点着色器编译器的优化错误。