OpenGLでは、一つのプログラムに複数のシェーダーを含めることができます。
# version 410
float someFunction() {
return 1.0;
}
# version 410
float someFunction(); // 実装はmodule.glslにあるので宣言のみ
void main() {
gl_Position = vec4(someFunction());
}
auto attachShader = [](GLuint program, GLenum type, std::string filepath) {
GLuint shaderObj = glCreateShader(type);
std::string source = loadStringFromFile(filepath); // なんかファイル読み込み処理
const char* sptr = source.c_str();
int ssize = source.size();
glShaderSource(shaderObj, 1, &sptr, &ssize);
glCompileShader(shaderObj);
glAttachShader(program, shaderObj);
glDeleteShader(shaderObj);
};
// ひとつのシェーダープログラムに複数のバーテックスシェーダーオブジェクトをアタッチする
GLuint program = glCreateProgram();
attachShader(program, GL_VERTEX_SHADER, "module.glsl");
attachShader(program, GL_VERTEX_SHADER, "main.vert");
ところが、これをofShaderでやろうとすると動きません。
ofShader shader;
shader.setupShaderFromFile(GL_VERTEX_SHADER, "module.glsl");
shader.setupShaderFromFile(GL_VERTEX_SHADER, "main.vert");
shader.linkProgram();
[ error ] ofShader: checkProgramLinkStatus(): program failed to link
[ error ] ofShader: ofShader: program reports:
ERROR: No definition of someFunction in vertex shader
これは、ofShaderがシェーダーのタイプ(GL_VERTEX_SHADER,GL_GEOMETRY_SHADER,GL_FRAGMENT_SHADER,GL_COMPUTE_SHADER
)毎にひとつしかソースを保持しない作りになっていることが原因です。
// https://github.com/openframeworks/openFrameworks/blob/0.11.0/libs/openFrameworks/gl/ofShader.h#L272
class ofShader {
...
// GLenumがシェーダータイプ。unordered_mapなので各タイプ毎に一つしかShaderを保持できない
std::unordered_map<GLenum, Shader> shaders;
...
};
// https://github.com/openframeworks/openFrameworks/blob/0.11.0/libs/openFrameworks/gl/ofShader.cpp#L375
bool ofShader::setupShaderFromSource(ofShader::Source && source){
...
// 同じタイプのシェーダーを複数setupしようとすると、ここで前のが上書きされる
shaders[source.type] = { shaderId, std::move(source) };
...
}
ということなので、ofShader::shadersを使わずにglAttachShader
してやればOKですね。
auto attachShader = [](ofShader &shader, GLenum type, std::string filepath) {
GLuint shaderObj = glCreateShader(type);
std::string source = ofBufferFromFile(filepath).getText();
const char* sptr = source.c_str();
int ssize = source.size();
glShaderSource(shaderObj, 1, &sptr, &ssize);
glCompileShader(shaderObj);
glAttachShader(shader.getProgram(), shaderObj);
glDeleteShader(shaderObj);
};
ofShader shader;
shader.setupShaderFromFile(GL_VERTEX_SHADER, "main.vert");
attachShader(shader, GL_VERTEX_SHADER, "module.glsl");
shader.linkProgram();
これでエラーなくシェーダープログラムがリンクできました。
もちろん普通にofShader::begin
とofShader::end
で使えます。
ちなみに、setupShaderFromFile
とattachShader
の順序を逆にすると、shader.getProgram()
が空なので正しく動きません。
ofShader(GLuint program)
みたいな初期化ができると良いんですが・・・。
なお、ofShaderは独自で#include
をサポートしていてくれたり、OpenGLにも拡張機能でARB_shading_language_include
があったりしますので場合によってはそちらの方が使いやすいかもしれません。