Opengl着色器:如果条件错误地评估为假
Posted
技术标签:
【中文标题】Opengl着色器:如果条件错误地评估为假【英文标题】:Opengl shader: if condition wrongly evaluated to false 【发布时间】:2016-05-27 14:53:26 【问题描述】:我最近购买了“opengl 着色语言食谱”。 我正在尝试编写其中一个示例:特别是反射立方体贴图。
现在,我的软件崩溃了。
我可以将问题缩小到一个假设条件为真,但“假”部分似乎已被执行(更多细节即将到来)。
目标是:
画一个天空盒。 绘制一个物体(茶壶),它反映了天空盒。开发环境: 我在 2 台带有 QT5.6.0 的 PC(工作笔记本电脑和家庭游戏设备)上以及带有 QT 5.3.0 的笔记本电脑上进行了尝试。 QT 5.6.0是mingw编译的32位版本,5.3.0版本不清楚。
两台电脑都有英伟达卡。我不会详细介绍,因为我认为问题不在于那里(除非答案中另有说明)。
我已经移除了茶壶的drawcall。 我目前只画天空盒。这意味着“DrawSkyBox”布尔统一始终为真。这很重要。
顶点着色器是问题所在(或者至少是引发崩溃的那个)。 代码在这里(这是书中的代码/项目,不是我的,反正是复制/粘贴):
#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);
着色器仍在执行“else”部分。
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”(-> ReflectDir = reflect(-worldView, worldNorm );
替换为ReflectDir = VertexPosition;
),它可以完美运行! (我的意思是没有崩溃并显示天空盒)。
所以我重申问题是: 为什么着色器执行 else 部分?
伊马里翁
PS:这是一篇很长的文章,所以如果您达到这一点并提供任何答案,请提前感谢。
【问题讨论】:
如果“crash”是指“应用程序强制关闭”,那么在核心转储或 IDE 的调试器(如果您使用的话)中发现了什么? 感谢您的编辑 :)。我使用 QT Creator。它说“应用程序停止工作”,所以是的,这是一种强制关闭。这是绘图调用中的分段错误。 (在我的 cpp 中:glDrawElements(...) -> 在 qopenglfunctions.h 中:d_ptr->DrawElements(mode, count, type, indices); 请注意,d_ptr 已正确初始化(DrawElements 0x58790c60某些 GPU 可能会评估分支的两侧,然后丢弃其中一侧。见:http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html
这并不能解释为什么会发生崩溃。您归一化的向量是否可以为零,因此归一化会导致除以零?
【讨论】:
我怀疑是某种优化。至于标准化,那是正确的。我修改了这样的计算: ReflectDir = reflect(-worldView, vec3(0.0, 1.0, 0.0));它不会崩溃。我稍后会更深入地调查,我现在没有时间。无论如何谢谢你:) 也就是说,它不应该执行这部分。存在这个问题是因为对于 Skybox,这些向量没有被传递......它们没有被使用。【参考方案2】:我创建了 2 个程序来摆脱顶点着色器中的“if”。 我使用具有“if”的完全相同的片段着色器。
然后一切正常。
考虑到这是来自 Nvidia 顶点着色器编译器的优化错误,我很想关闭此问题。
【讨论】:
以上是关于Opengl着色器:如果条件错误地评估为假的主要内容,如果未能解决你的问题,请参考以下文章