注意:我将首先具体吐出我的问题,然后我会更好地解释我的场景。
在我的窗户上,使用我的英特尔驱动程序。我可以编译这两个(分别是顶点和片段着色器),但不能链接它们:
attribute vec4 vertex;
void main(void) {
gl_Position = vertex;
}
,
uniform sampler2D textures[2];
vec4 third(sampler2D texture) {
return texture2D(texture,vec2(0.5,0.5));
}
vec4 second(float a) {
return third(textures[0]);
}
void main(void) {
gl_FragColor = second(0.0);
}
我在链接时收到错误消息(链接状态为假),但信息日志为空。
问题是这个着色器在我的 Linux 中使用相同的 GPU 运行良好,并且在我的 NVIDIA 中运行良好。只有在使用英特尔驱动程序的 Windows 中,我才会遇到这个问题。我的 OpenGL 通知以下有关驱动程序的信息:
GL_VENDOR Intel
GL_RENDERER Intel(R) HD Graphics Family
GL_VERSION 3.0.0 - Build 8.15.10.2253
GL_SHADING_LANGUAGE_VERSION 1.30 - Intel Build 8.15.10.2253
有趣的是,看起来无关紧要的修改使程序能够正确链接,到目前为止我发现了一些:
- 如果第二个函数没有接收到任何参数,它就会起作用。
- 如果 main() 直接调用第三个,它可以工作。
- 如果制服不是数组,它是单个元素,它可以工作。
- 如果不是将 sampler2D 从第二个传递到第三个,而是传递一个索引,第三个将用于访问纹理,它可以工作。
我的问题是如何使它工作但保持相同的语义?我需要三个函数,我需要third
和main
函数不使用制服,我需要只有second
函数知道制服。另外,我需要second
andthird
函数接收参数。在我的真实世界场景中,第二个和第三个函数用它收到的值做了很多事情。
只是为了澄清,我在做什么需要这个:
在我正在开发的框架中,使用了三种不同的片段着色器。第一个(main
在我的参考代码中)是用户提供的,这个可以调用second
框架中定义的函数(在我的参考代码中)。第三级也是用户提供的,但着色器只编译一次,不知道它会被使用多少次以及它的哪些值,它second
负责分配正确数量的缓冲区并third
为每个缓冲区调用。
这是我用来测试着色器的代码:
#include <SDL.h>
#include <GL/glew.h>
#include <iostream>
#include <vector>
#include <cassert>
#include <sstream>
#include <stdexcept>
#define WIDTH 800
#define HEIGHT 640
void warn(const std::exception& e) {
#ifndef NDEBUG
std::cerr << "Warning: " << e.what() << std::endl;
#endif
};
namespace {
std::string tostr(unsigned a) {
std::stringstream ss;
ss << a;
return ss.str();
}
std::string errorname(unsigned a) {
switch(a) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
}
return "";
}
}
void checkGlErrorImpl(unsigned line, const char* file) {
GLenum curerr = glGetError();
if( curerr == GL_NO_ERROR )
return;
auto err = std::runtime_error(std::string("OpenGL ")+errorname(curerr)+" error on "+file+":"+tostr(line));
warn(err);
throw err;
}
#define checkGlError() checkGlErrorImpl(__LINE__,__FILE__)
int create_shader(unsigned type, const char* shaderSource) {
unsigned id = glCreateShader(type);
const char* shaderSources[2] = {"#version 130\n",shaderSource};
glShaderSource(id,2,shaderSources,NULL);
glCompileShader(id);
GLint compileStatus;
glGetShaderiv(id, GL_COMPILE_STATUS, &compileStatus);
int msgLength;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &msgLength);
char* msg = new char[msgLength];
glGetShaderInfoLog(id, msgLength, &msgLength, msg);
std::cout << "(" << id << ") " << msg << std::endl;
std::runtime_error except(std::string("Error on compiling shader:\n")+msg);
delete[] msg;
if( compileStatus == GL_FALSE ) {
warn(except);
throw except;
}
checkGlError();
return id;
};
int create_program(const std::vector<int>& shaders) {
int id = glCreateProgram();
checkGlError();
for( unsigned int i=0; i< shaders.size(); ++i ) {
glAttachShader(id,shaders[i]);
}
glLinkProgram(id);
GLint linkStatus=-1;
glGetProgramiv(id, GL_LINK_STATUS, &linkStatus);
assert(linkStatus != -1);
checkGlError();
if( linkStatus == GL_FALSE ) {
int msgLength=-1;
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &msgLength);
assert( msgLength != -1 );
char* msg = new char[msgLength+1];
msg[0] = '\0';
std::cout << "Buffer(" << msgLength+1 << ")" << msg << std::endl;
glGetProgramInfoLog(id, msgLength+1, &msgLength, msg);
std::cout << "Second length " << msgLength << std::endl;
std::cout << "Log " << msg << std::endl;
std::string errormsg("Error on linking shader: ");
errormsg += msg;
// delete[] msg;
auto err = std::runtime_error(errormsg);
warn(err);
throw err;
}
checkGlError();
return id;
}
int main(int argc, char** argv) {
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
throw __LINE__;
}
int video_flags;
video_flags = SDL_OPENGL;
video_flags |= SDL_GL_DOUBLEBUFFER;
video_flags |= SDL_HWSURFACE;
video_flags |= SDL_HWACCEL;
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
SDL_Surface* surface = SDL_SetVideoMode( WIDTH, HEIGHT, 24, video_flags );
if( surface == NULL )
throw __LINE__;
unsigned width = WIDTH;
unsigned height = HEIGHT;
GLenum err = glewInit();
if (GLEW_OK != err) {
std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
throw __LINE__;
}
std::vector<int> shaders;
shaders.push_back( create_shader(GL_VERTEX_SHADER,
"attribute vec4 vertex;\n"
"void main(void) {\n"
" gl_Position = vertex;\n"
"}\n"
));
shaders.push_back( create_shader(GL_FRAGMENT_SHADER,
"uniform sampler2D textures[2];\n"
"vec4 third(sampler2D texture) {\n"
" return texture2D(texture,vec2(0.5,0.5));\n"
"}\n"
"vec4 second(float a) {\n"
" return third(textures[0]);\n"
"}\n"
"\n"
"void main(void) {\n"
" gl_FragColor = second(0.0);\n"
"}\n"
));
int program = create_program(shaders);
try {
while( true ) {
SDL_Event event;
while( SDL_PollEvent(&event) ) {
switch( event.type ) {
case SDL_QUIT:
throw 0;
break;
}
}
SDL_Delay(10);
}
} catch( int returnal) {
return returnal;
}
return 0;
};
它依赖于 GLEW、SDL、OpenGL 和 C++11。