WindowsでOpenGLES --- 三角形描画
下記記事にてGLFWで、OpenGLESが初期化されたウィンドウを作成しました。
WindowsでOpenGLES --- GLFW導入 - 何でもプログラミング
今回はとりあえず三角形を描画してみたいと思います。
shared_ptrでリソースの管理
OpenGLではCreateとDeleteをきちんと管理する必要があります。(glCreateProgram() → glDeleteProgram(GLuint)など)
手動で管理するのは面倒なので、shared_ptrに解放を任せてみます。
shared_ptrにはカスタムデリータが設定できるので、そこで解放を行います。
OpenGLにはglCreateProgramのような単体リソース作成と、glGenTexturesのような複数リソース作成の2パターンがあるので、2つ定義してみました。
今回は暗黙キャストが利用したかったのでクラスを定義しましたが、不必要であればshared_ptrそのもので良いと思います。
class GLResource { std::shared_ptr<GLuint> _resource; public: GLResource() {} // glCreateProgram, glDeleteProgramなど用 GLResource(GLuint resource, std::function<void(GLuint)> glDelete) { if (resource != 0) _resource.reset(new GLuint(resource), [=](GLuint* x) { glDelete(*x); delete x; }); } // glGenTextures、glDeleteTextursなど用 GLResource(std::function<void(GLsizei, GLuint*)> glGen, std::function<void(GLsizei, GLuint*)> glDelete) { GLuint resource = 0; glGen(1, &resource); if (resource != 0) _resource.reset(new GLuint(resource), [=](GLuint* x) { glDelete(1, x); delete x; }); } operator bool() { return (bool)_resource; } operator GLuint() { return *_resource; } };
Program(Shader)作成
シェーダコードをコンパイルしてプログラムを作成する関数を定義します。
CompileShader、PrintProgramLogは後述いたします。
GLResource CreateProgram(std::string vertexCode, std::string fragmentCode) { // Vertexシェーダコンパイル GLResource vertexShader(glCreateShader(GL_VERTEX_SHADER), glDeleteShader); assert(vertexShader); CompileShader(vertexShader, vertexCode.c_str()); // Fragmentシェーダコンパイル GLResource fragmentShader(glCreateShader(GL_FRAGMENT_SHADER), glDeleteShader); assert(fragmentShader); CompileShader(fragmentShader, fragmentCode.c_str()); GLResource program(glCreateProgram(), glDeleteProgram); assert(program); // リンクプログラム glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); PrintProgramLog(program); glDetachShader(program, fragmentShader); glDetachShader(program, vertexShader); // リンクエラー確認 GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); assert(status != GL_FALSE); return program; }
CompileShader
void CompileShader(GLuint shader, const GLchar* code) { glShaderSource(shader, 1, &code, NULL); glCompileShader(shader); PrintShaderLog(shader); // コンパイルエラー確認 GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); assert(status != GL_FALSE); } void PrintShaderLog(GLuint shader) { GLint length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); if (0 < length) { std::vector<GLchar> log(length); glGetShaderInfoLog(shader, log.size(), &length, log.data()); printf("%s\n", log.data()); } }
PrintProgramLog
void PrintProgramLog(GLuint program) { GLint length; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); if (0 < length) { std::vector<GLchar> log(length); glGetProgramInfoLog(program, log.size(), &length, log.data()); printf("%s\n", log.data()); } }
main関数
入力ポリゴンを赤色に塗りつぶすだけのGLSLで、三角形を描画しています。
ウィンドウのサイズ変更に追従するよう、コールバックを登録しています。
GLFWのコールバック登録は関数ポインタであり、ローカル変数キャプチャのためにdrawグローバル変数を用意しました。
std::function<void()> draw; int main() { assert(glfwInit()); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GLFWwindow* window = glfwCreateWindow(200, 200, "Triangle", NULL, NULL); assert(window); glfwMakeContextCurrent(window); // Program準備 std::string vertexCode = "#version 300 es\n" "layout (location = 0) in vec4 position;\n" "void main() { gl_Position = position; }"; std::string fragmentCode = "#version 300 es\n" "precision mediump float;\n" "out vec4 fragColor;\n" "void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); }"; GLResource program = CreateProgram(vertexCode, fragmentCode); glUseProgram(program); // 頂点データ設定 glEnableVertexAttribArray(0); const GLfloat positions[] = { 0.0, 1.0, 1.0, -1.0, -1.0, -1.0 }; glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, positions); // Draw関数定義 draw = [&] { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 3); glfwSwapBuffers(window); }; // コールバック登録 glfwSetWindowSizeCallback(window, [](GLFWwindow*, int w, int h) { glViewport(0, 0, w, h); }); glfwSetWindowRefreshCallback(window, [](GLFWwindow*) { draw(); }); // メインループ while (glfwWindowShouldClose(window) == GL_FALSE) { glfwWaitEvents(); } glfwTerminate(); return 0; }