OpenGL 4.xを少し

とりあえず横線を書くだけ。

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へ乗り換える気力が足らない。

広告

OpenGL 4.xを少し」への2件のフィードバック

  1. 多分ですが,謎のシェーダコンパイルエラーが出る理由は,
    loadFile()で読み込んだ文字列の末尾に null が入ってないからです.

    末尾に null を付けるか,glShaderSource側で文字列の長さを指定すれば,
    謎のエラーは消えると思います.

    • 情報ありがとうございます。仰る通りです。
      実はこれを書いた後GeForce環境で動かしたところ、’unexpected $undefined at token “”‘といういかにも基本的なミスがあるかのようなエラーログが出たので気づきましたが、時間がなくて放置していました。申し訳ありません。
      他にもVisualStudioではそのままではビルドできなかったり、実はglGetProgramBinary()が使え(るようだっ)たり、いろいろ問題がある状態なので、修正します。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中