我最近买了" 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:这是一个很长的阅读,所以如果你达到了这一点并得到任何答案,请提前感谢。
答案 0 :(得分:0)
某些GPU可能会评估某个分支的两端,然后丢弃一个分支。请参阅:http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html
这并不能解释为什么你会崩溃。您规范化的向量是否可以为零,以便归一化导致除以零?
答案 1 :(得分:0)
我创建了2个程序来摆脱顶点着色器中的“if”。 我使用完全相同的片段着色器,它具有“if”。
然后一切顺利。
我很想解决这个问题,因为这是来自Nvidia顶点着色器编译器的优化错误。