间歇性GLSL顶点着色器编译错误
intermittent GLSL vertex shader compile errors
我在编译顶点着色器时遇到了间歇性错误,为新创建的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
,你就无法更改窗口的像素格式。我确保每次都使用相同的像素格式)
添加:在修剪渲染代码时,我发现如果我注释掉行
glEnable( GL_VERTEX_PROGRAM_TWO_SIDE );
然后错误就消失了。但随着更多真实世界渲染(例如纹理)的恢复,这似乎没有什么不同。
2014年3月7日添加:我终于制作了一个独立的Visual C++项目,它可以在某些情况下重现错误,这样任何感兴趣的人都可以尝试:ShaderBugTest项目要使错误重现,你需要设置Microsoft的"应用程序验证程序"来监视堆错误。随附的Read-Me有更详细的复制步骤。
您的示例中有很多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调用后报告任何异常情况,则应停止该程序,以便隔离第一个错误发生的位置。编译失败完全有可能是之前发生的事情的级联效应。
使用glGetShaderInfoLog查看编译错误消息。有关示例,请参见http://www.opengl.org/wiki/Shader_Compilation#Shader_error_handling
看起来您没有向gl_Color
写入任何内容。
gl_Color
属性在不同的地方可以表示不同的事物。
在顶点着色器中,gl_Color
表示用户使用glColor*
或glColorPointer
调用传递的逐顶点颜色属性。
在片段着色器中,gl_Color
表示要渲染的三角形面的插值颜色。
然而,这是一个固定的管道功能,gl_Color
在现代OpenGL中已被弃用。这可能是你犯错误的原因。您需要使用以下代码获得编译器错误消息:
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版本是什么?是核心还是兼容性配置文件?
- std::is_base_of表示ctor编译错误
- Qt5:使用QCommandLineParser类时出现奇怪的编译错误
- 如何修复sfml c++代码编译错误
- 使用 MATLAB 编码器生成C++代码:编译错误"undefined reference to `rgb2gray_tbb_real64'"
- 使用外部SDK工具链文件在VisualStudio上生成项目编译错误
- vscode下的Arduino代码出现意外编译错误
- 第三方 API 中的编译错误 - Visual Studio
- std::cout输出int时出现编译错误
- 奇怪的代码抛出编译错误模板< J,int aSize=10> C2143:语法错误:在"<"之前缺少";"
- 提升图广度优先搜索前置编译错误
- C++ 中的编译错误:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- 使用带有 ref 参数的成员函数创建线程时出现编译错误
- 我在C++中遇到了这个奇怪的编译错误
- 在C++中使用 Catch 测试框架编译错误"error: expected ';' at end of declaration list"
- 使用 std::enable_if 限制派生类的模板参数时出现编译错误
- 现代OpenGL和GLEW Libraray的编译错误
- C++ 编译错误:意外的类型名称"字符串":预期的表达式
- C ++程序编译错误,找不到/访问文件
- 使用直接大括号初始化时,C++ 编译错误"声明末尾的预期";"
- 为什么传递非静态成员函数会导致编译错误?