とりあえず横線を書くだけ。
Mac OS 10.9.2 (Marvericks) + Xcode 5.1 + Mac mini late 2012 (Intel HD Graphics 4000)
多少手直しすればWindowsでも動くはず(注意:Windows版のIntelドライバはOpenGL 4.0までしか対応していないので、バージョン指定を変えること)
OpenGLリソースの解放処理がないので注意
C++コード
// OpenGL test // No need to use GLEW on Mac OS. #define USE_GLEW 0 // if GLEW does not compiled as .dll(Windows) or .dylib(Mac OS), define this. #define GLEW_STATIC #include <unistd.h> #include <iostream> #include <fstream> #include <memory> #include <sstream> #if USE_GLEW # include <GL/glew.h> #else # define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED # include <OpenGL/gl3.h> #endif #include <GLFW/glfw3.h> #ifdef _WIN32 #pragma comment( lib, "glfw3.lib" ) #pragma comment( lib, "opengl32.lib" ) #if USE_GLEW # ifdef NDEBUG # pragma comment( lib, "glew32s.lib" ) # else # pragma comment( lib, "glew32sd.lib" ) # endif #endif #endif // _WIN32 #define SCREEN_WIDTH (800) #define SCREEN_HEIGHT (600) #define SHADER_DIR "/path/to/my/shader/files/dir" namespace { std::unique_ptr<char[]> loadFile(const char* path) { auto fp = std::ifstream(path, std::ios::in | std::ios::binary); if(!fp) throw "File cannot open."; auto len = fp.seekg(0, std::ios::end).tellg(); if(len <= 0) throw "File is empty."; fp.seekg(0, std::ios::beg); std::unique_ptr<char []> cs(new char[len]); fp.read(cs.get(), len); if(fp.fail()) throw "Reading file failed."; if(fp.gcount() != len) throw "File cannot read to the end."; return cs; }; const auto glshader_deletor = [](GLuint *shader) { glDeleteShader(*shader); delete shader; }; std::unique_ptr<GLuint, decltype(glshader_deletor)> loadShader(const char* code, GLenum type) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &code, nullptr); glCompileShader(shader); GLint compiled; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if(compiled == GL_FALSE) { GLchar log[1024]; GLsizei len = sizeof(log) / sizeof(*log); glGetShaderInfoLog(shader, len, &len, log); std::cerr << "glCompileShader() gets error." << std::endl << log; shader = GL_INVALID_VALUE; throw "Shader compile error."; } std::unique_ptr<GLuint, decltype(glshader_deletor)> g(new GLuint(shader), glshader_deletor); return g; } } struct DrawContext { struct { GLuint tess; } prog; struct { GLuint vaoNull; } common; }; static void error_callback(int error, const char* description) { std::cerr << "GLFW gets error(" << error << ")." << std::endl << description; throw "GLFW error."; } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { glfwSetWindowShouldClose(window, GL_TRUE); } } static void init(DrawContext &context) { // add current diretory to env { std::stringstream ss; ss << getenv("PATH") << ":" << SHADER_DIR; setenv("PATH", ss.str().c_str(), 1); } // create shaders { auto srcVS = loadFile("VertexShader.glsl"); auto srcFS = loadFile("FragmentShader.glsl"); context.prog.tess = glCreateProgram(); auto vs = loadShader(srcVS.get(), GL_VERTEX_SHADER); auto fs = loadShader(srcFS.get(), GL_FRAGMENT_SHADER); glAttachShader(context.prog.tess, *vs.get()); glAttachShader(context.prog.tess, *fs.get()); glLinkProgram(context.prog.tess); GLint linked; glGetProgramiv(context.prog.tess, GL_LINK_STATUS, &linked); if(linked == GL_FALSE) { GLsizei len; GLchar log[1024]; glGetProgramInfoLog(context.prog.tess, sizeof(log) / sizeof(*log), &len, log); std::cerr << "glLinkProgram() gets error." << std::endl << log; throw "Shader link error."; } } // create common { glGenVertexArrays(1, &context.common.vaoNull); } } static void paint(DrawContext &cont) { glClearColor(0.0f, 0.0f, 0.5f, 1.0f); glClearDepth(1.0f); glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //glEnable(GL_CULL_FACE); //glCullFace(GL_BACK); glUseProgram(cont.prog.tess); glBindVertexArray(cont.common.vaoNull); glDrawArrays(GL_LINES, 0, 2); GLint error = glGetError(); if(error != GL_NO_ERROR) { std::cout << "OepnGL gets error(0x" << std::hex << ")." << std::endl; } } int main(int argc, char* argv[]) { glfwSetErrorCallback(error_callback); if (!glfwInit()) { return -1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_RED_BITS, 8); glfwWindowHint(GLFW_GREEN_BITS, 8); glfwWindowHint(GLFW_BLUE_BITS, 8); glfwWindowHint(GLFW_ALPHA_BITS, 8); glfwWindowHint(GLFW_DEPTH_BITS, 24); glfwWindowHint(GLFW_STENCIL_BITS, 0); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "GL Sample", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); glfwSwapInterval(1); #if USE_GLEW auto glewErr = glewInit(); if (glewErr != GLEW_OK) { puts((const char*)glewGetErrorString(glewErr)); return 1; } // glewInit() occurs glGetError() == GL_INVALID_ENUM GLint glErr; while((glErr = glGetError()) != GL_NO_ERROR) { } #endif DrawContext cont; init(cont); while (!glfwWindowShouldClose(window)) { paint(cont); glfwSwapBuffers(window); glfwPollEvents(); } glfwDestroyWindow(window); glfwTerminate(); return 0; } <pre>
フラグメントシェーダ
</pre> #version 410 core layout(location = 0) out vec4 fragColor; void main() { vec4 color = vec4(0.9, 0.2, 0.1, 1.0); fragColor = color; } <pre>
頂点シェーダ
</pre> #version 410 core void main() { vec4 pos = vec4(0.0, 0.0, 0.0, 1.0); if(gl_VertexID == 0) { pos.xy = vec2(-1.0, 0.0); } else if(gl_VertexID == 1) { pos.xy = vec2(1.0, 0.0); } else if(gl_VertexID == 2) { pos.xy = vec2(0.0, 1.0); } gl_Position = pos; } <pre>
■謎のシェーダコンパイルエラー
3回に1回程度の確率で、glCompileShader()が失敗する。
ERROR: 0:25: ‘<’ : syntax error syntax error
シェーダコードのどこにも’<’という文字は存在しない。しかも、コードを少し変えると’<’が’_’とか’Apple_f’とか’cd’とか全く見覚えのない文字になる。そもそもsyntax errorが2回出てくる時点で挙動が怪しい。ドライバのバグとしか思えないが、検索しても情報が全く出てこないので困っている。
GL_ARB_get_program_binaryも非対応のようなので、事前コンパイルで逃げることもできない。HLSLは素晴らしかった。
GLEWを使うとなぜかエラーの頻度が上がるので、ON/OFFできるようにした。それでもコンパイルが通らないときは、シェーダ末尾に適当な改行を入れると大抵動く。それでも動かないときは、Intelドライバへの信仰心が足らないか、GeForceかRadeonへ乗り換える気力が足らない。
