注:最初に私の問題を具体的に吐き出し、次に私のシナリオをよりよく説明します。
Windowsで、Intelドライバーを使用します。これら2つ(それぞれ頂点シェーダーとフラグメントシェーダー)をコンパイルできますが、リンクできません:
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);
}
リンクでエラーが発生しました(リンクステータスはfalseです)が、情報ログが空です。
問題は、このシェーダーが同じGPUを搭載したLinuxで正常に動作し、NVIDIAでも正常に動作することです。Intelドライバーを使用している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
面白いことに、無関係と思われる変更を加えると、プログラムが正しくリンクされます。これまでにいくつか見つけました。
- 2番目の関数がパラメーターを受け取らない場合、それは機能します。
- main()が3番目を直接呼び出す場合、それは機能します。
- ユニフォームが配列でない場合、それは単一の要素であり、機能します。
- sampler2Dを2番目から3番目に渡す代わりに、3番目がテクスチャへのアクセスに使用するインデックスを渡すと機能します。
私の質問は、それをどのように機能させるが、同じセマンティクスを維持するかです。3つの関数が必要です。third
とmain
関数がユニフォームを使用しないようにする必要がありsecond
ます。関数だけがユニフォームを知っている必要があります。また、second
andthird
関数がパラメーターを受け取る必要があります。私の実際のシナリオでは、2番目と3番目の関数は、受け取った値を使用して多くのことを実行します。
そして明確にするために、私がしていることにはこれが必要です:
私が開発しているフレームワークでは、3つの異なるフラグメントシェーダーが使用されています。最初のもの(main
私の参照コード内)は、ユーザーが提供するものであり、これsecond
は、フレームワークで定義された関数(私の参照コード内)を呼び出す場合があります。3番目のレベルもユーザーが提供しますが、シェーダーは1回だけコンパイルされ、使用回数と値がわからない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に依存します。