我一直在编写一个顶点着色器的间歇性错误,为新创建的OpenGL上下文的第一次渲染做准备。它是通常在同一硬件上工作的相同顶点着色器。失败后,glGetShaderInfoLog
返回的信息日志通常会显示:
Vertex shader failed to compile with the following errors:
是的,这就是整件事。它发生在Windows上,包括ATI和NVidia GPU,但我主要是在ATI上进行测试。如果我在Visual Studio调试器下运行,则可能会在glCompileShader
或随后的glGetShaderiv
调用中检测到访问冲突。
我没有看到Mac上的错误,但由于重现它并不总是很容易,我不能确定它不会发生。
我注意到如果我在创建上下文时没有调用wglShareLists
,则错误消失(或者至少我不能轻易地重现它)。我认为这意味着一些不良信息正从先前创建的(可能是先前破坏的)OpenGL上下文流向新的。然而,我一直在修剪东西,直到没有太多应该分享的东西:没有纹理对象,显示列表,VBO,PBO。据我所知,唯一可以分享的东西是着色器和程序对象。
在为窗口创建OpenGL上下文之后,我大致初始化着色:
// Program initialization
GLint vertexShader = glCreateShader( GL_VERTEX_SHADER );
glShaderSource( vertexShader, 1, &vertexShaderSource, NULL );
glCompileShader( vertexShader );
GLint status;
glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );
if (status == GL_TRUE)
{
GLint fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( fragmentShader, 1, &fragShaderSource, NULL );
glCompileShader( fragmentShader );
glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );
if (status == GL_TRUE)
{
GLint program = glCreateProgram();
if (program != 0)
{
glAttachShader( program, vertexShader );
glAttachShader( program, fragmentShader );
glLinkProgram( program );
glDetachShader( program, fragmentShader );
glDetachShader( program, vertexShader );
glDeleteShader( fragmentShader );
// check for link success...
}
}
}
else
{
char logBuf[1024];
glGetShaderInfoLog( vertexShader, sizeof(logBuf), NULL, (GLchar*)logBuf );
LogToFile( (char*)logBuf );
}
然后我渲染多个帧,并在某些时候清理
glDeleteProgram( program );
glDeleteShader( vertexShader );
并销毁OpenGL上下文。稍后,我在同一个窗口中创建一个新的OpenGL上下文,以相同的方式初始化它,但顶点着色器无法编译。
如果我不渲染任何几何体,我就无法重现错误,但渲染单点就足够了。
它仍然使用简化的顶点着色器:
#version 120
void main()
{
gl_Position = ftransform();
gl_FrontColor = gl_Color;
gl_BackColor = gl_Color;
}
和一个简化的片段着色器:
#version 120
void main()
{
gl_FragColor = gl_Color;
}
我尝试了AMD的CodeXL OpenGL调试器,将其设置为打破错误。它只是告诉我编译失败,我已经知道了。
失败的OpenGL上下文位于一个窗口上,该窗口先前已创建,成功呈现和销毁了不同的上下文。重新使用这样的窗口没有错,是吗? (我知道一旦你调用了SetPixelFormat
,你就无法改变窗口的像素格式。我确保每次都使用相同的像素格式。)
ADDED:在减少渲染代码的同时,我发现如果我注释掉了这行
glEnable( GL_VERTEX_PROGRAM_TWO_SIDE );
然后错误就消失了。但随着更多真实世界的渲染(例如,纹理)恢复,它似乎没有什么区别。
ADDED 3/7/2014:我最终制作了一个独立的Visual C ++项目,可以在某些情况下重现该bug,以便任何感兴趣的人都可以尝试:ShaderBugTest project为了使错误可以重现,你需要设置Microsoft的“应用程序验证程序”来监视堆错误。随附的自述文件有更详细的步骤来重现。
答案 0 :(得分:5)
你的例子有很多GL对象的分配和释放,但几乎没有错误检查。
vertexShader = glCreateShader( GL_VERTEX_SHADER );
需要检查返回值vertexShader,以确保它有效。
glGetShaderiv( vertexShader, GL_COMPILE_STATUS, &status );
您获得此值,但不显示您正在检查状态。
program = glCreateProgram();
同样,在开始使用它之前需要检查返回值。
glAttachShader( program, vertexShader );
fragmentShader = glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource( fragmentShader, 1, &fragShaderSource, NULL );
glCompileShader( fragmentShader );
您应该在将片段着色器和顶点着色器附加到程序之前创建它们。如果出现故障,它可以减少清理工作。
glGetShaderiv( fragmentShader, GL_COMPILE_STATUS, &status );
再次,在获取后检查编译状态。编译失败,但到目前为止我们还不知道哪个着色器无法编译。它是片段着色器还是顶点着色器。
glAttachShader( program, fragmentShader );
glDeleteShader( fragmentShader );
在这里删除着色器很奇怪。是的,它应该只被标记为删除,因为它实际上附加到程序,但这似乎仍然是一种危险的方式。此外,由于没有明显的原因,您采用了不同的方法来管理片段着色器而不是顶点着色器。
glLinkProgram( program );
glDetachShader( program, fragmentShader );
glDetachShader( program, vertexShader );
同样,对我来说,这对着色器管理来说是一种莫名其妙的方法。再次,同样处理片段着色器和顶点着色器。
glDeleteProgram( program );
glDeleteShader( vertexShader );
为什么对片段着色器进行不同的处理?
在您诊断问题时,您应该使用glGetError()
并在每次GL呼叫之后报告任何异常情况时停止该程序,以便您可以隔离发生第一个错误。编译失败完全有可能是之前发生的事情的级联效应。
答案 1 :(得分:2)
使用glGetShaderInfoLog查看编译错误消息。有关示例,请参阅http://www.opengl.org/wiki/Shader_Compilation#Shader_error_handling
答案 2 :(得分:2)
看起来你没有给gl_Color
写任何东西。
gl_Color
属性在不同的地方可能意味着不同的东西。
在您的顶点着色器中,gl_Color
表示用户使用glColor*
或glColorPointer
调用传递的每顶点颜色属性。
在片段着色器中,gl_Color
表示正在渲染的三角形的面对的插值颜色。
但是,这是一个固定管道功能,现代OpenGL中不推荐使用gl_Color
。这可能是您的错误背后的原因。您需要使用以下代码获取编译器错误消息:
GLint SuccessFlag = 0;
GLsizei Length = 0;
GLsizei MaxLength = 0;
glGetShaderiv( ShaderID, GL_COMPILE_STATUS, &SuccessFlag );
glGetShaderiv( ShaderID, GL_INFO_LOG_LENGTH, &MaxLength );
char* Log = ( char* )alloca( MaxLength );
glGetShaderInfoLog( ShaderID, MaxLength, &Length, Log );
将日志添加到您的问题将有助于更好地诊断您的问题。
您使用的是哪个版本的OpenGL?它是核心还是兼容性配置文件?