«(1) GLFW で OpenGL を使う 最新 (3) OpenGL のバージョンとプロファイルの指定»

床井研究室

※このブログは遅くとも 2027 年 3 月に管理者の定年退職により閉鎖します (移転先は管理者本人共々模索中)

■ 2012年09月07日 [OpenGL][GLFW] (2) エラー処理, 初期設定の追加

2012年09月20日 10:57更新

起動時のエラー処理

前回のプログラムでは GLFW の関数のエラーチェックを行っていませんでした. ウィンドウが開けなければ while ループは1回も回ること無く通過し, その後 glfwTerminate() を実行して main() が終了するので問題は無いのですが, glfwInit() が失敗したときに以降の GLFW の関数を実行しようとするのは実害は無いとしてもなんか嫌なので, 一応エラーチェックを入れてみます.

#include <GL/glfw.h>
 
int main(int argc, const char * argv[])
{
  glfwInit();
  
  glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW);
  
  while (glfwGetWindowParam(GLFW_OPENED))
  {
    /*
    ** ここで OpenGL による描画を行う
    */
    
    glfwSwapBuffers();
  }
  
  glfwTerminate();
  
  return 0;
}

glfwInit() や glfwOpenWindow() は成功すると GL_TRUE, 失敗すると GL_FALSE を返すので, 失敗したときにはメッセージを出して exit() するようにします. また glfwOpenWindow() の場合は既に glfwInit() を実行しているので, exit() する前に glfwTerminate() を実行します.

ところが, glfwTerminate() はプログラムの終了時に自動的に呼び出されるよう glfwInit() を実行するときに atexit() で登録される (とコメントでご指摘いただいて, 自分でもソースを確認した) ので, main() から return する場合や明示的に exit() する場合は呼ばなくてもいいみたいです. glfwTerminate() を呼びなさなければならないのは, これら以外の方法でプログラムを無理やり終了させたり, 開いたウィンドウをすべて閉じ, 走らせたすべてのスレッドを止めて, もう一度 glfwInit() からやり直すような場合でしょうか.

あと, ついでに最後の return 0; を return EXIT_SUCCESS; に変更します. これはどうでもいいんですけど.

#include <iostream>
#include <cstdlib>
#include <GL/glfw.h>
 
int main(int argc, const char * argv[])
{
  // GLFW を初期化する
  if (!glfwInit())
  {
    // 初期化に失敗した
    std::cerr << "Can't initialize GLFW." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // GLFW のウィンドウを開く
  if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW))
  {
    // ウィンドウが開けなかった
    std::cerr << "Can't open GLFW window." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // 図形を表示する
  while (glfwGetWindowParam(GLFW_OPENED))
  {
    /*
    ** ここで OpenGL による描画を行う
    */
    
    glfwSwapBuffers();
  }
  
  return EXIT_SUCCESS;
}

画面消去

このプログラムでは画面に何も描いていないので, ウィンドウのオープンに成功しても, 開いたウィンドウには何も描かれていないか, ゴミみたいなものが描かれると思います. そこで, while による繰り返しのところで glClear() を実行して, 画面消去を行います.

#include <iostream>
#include <cstdlib>
#include <GL/glfw.h>
 
int main(int argc, const char * argv[])
{
  // GLFW を初期化する
  if (!glfwInit())
  {
    // 初期化に失敗した
    std::cerr << "Can't initialize GLFW." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // GLFW のウィンドウを開く
  if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW))
  {
    // ウィンドウが開けなかった
    std::cerr << "Can't open GLFW window." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // 図形を表示する
  while (glfwGetWindowParam(GLFW_OPENED))
  {
    // 画面消去
    glClear(GL_COLOR_BUFFER_BIT);
    
    /*
    ** ここで OpenGL による描画を行う
    */
    
    glfwSwapBuffers();
  }
  
  return EXIT_SUCCESS;
}
void glClear(GLbitfield mask)
ウィンドウ全体を以前に実行した glClearColor() や glClearDepth(), glClearStencil(), glClearAccum() で設定した値で塗りつぶします. mask には, カラーバッファを塗りつぶす場合には GL_COLOR_BUFFER_BIT, デプスバッファを塗りつぶす場合には GL_DEPTH_BUFFER_BIT, ステンシルバッファを塗りつぶす場合には GL_STENCIL_BUFFER_BIT を指定します. これらはビット OR "|"" を使って複数指定でき, その場合は複数のバッファが同時に塗りつぶされます. またカラーバッファの塗りつぶしでは, Multiple Render Target に指定したすべてのカラーバッファが同時に塗りつぶされます.

初期設定

glClear() による塗りつぶし色のデフォルトは黒なので, glClearColor() を使って塗りつぶし色を変更してみます. これは描画のたびに行う必要はないので, while の前に行います. 多分, 他にもここでやりたいことはあると思うので, init() という関数で glClearColor() を実行して, init() を while の直前で呼び出すことにします.

#include <iostream>
#include <cstdlib>
#include <GL/glfw.h>
 
// 初期設定
static void init(void)
{
  // 背景色
  glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
 
int main(int argc, const char * argv[])
{
  // GLFW を初期化する
  if (!glfwInit())
  {
    // 初期化に失敗した
    std::cerr << "Can't initialize GLFW." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // GLFW のウィンドウを開く
  if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW))
  {
    // ウィンドウが開けなかった
    std::cerr << "Can't open GLFW window." << std::endl;
    exit(EXIT_FAILURE);
  }
  
  // 初期設定
  init();
  
  // 図形を表示する
  while (glfwGetWindowParam(GLFW_OPENED))
  {
    // 画面消去
    glClear(GL_COLOR_BUFFER_BIT);
    
    /*
    ** ここで OpenGL による描画を行う
    */
    
    glfwSwapBuffers();
  }
  
  return EXIT_SUCCESS;
}
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
glClear() でカラーバッファを塗りつぶす値を設定します. red, green, blue, および alpha はカラーバッファの各チャネルを塗りつぶす値で, [0,1] の実数値 (単精度) を指定します.

ターゲットが分からない

これを書いているうちに, どうしても「手抜きOpenGL」の経験に引きずられてしまうことが気になってます. 「手抜きOpenGL」は実験の指導書だったので学生さんという明確なターゲットが存在したのですが, 今回のこれは自分が GLUT の代替品を探し始めたことがモチベーションなので, ターゲットは, しいて言えば自分です. ですが, 一応, 研究室の学生さんにも読んでもらおうと思ってるので, それなりに気をつけて書いています. そんな風にターゲットが揺らいでいるせいか, 今回の文章は粒度がばらついている気がしますし, 自分の流儀を押し付けてるところが目について, 自分で読んでて気持ちが悪いです. ふぅ.

コメント(2) [コメントを投稿する]
通りすがり 2012年09月18日 03:09

最後の方で書かれている“流儀”に関してですが、例えば Reference manualを読むと、glfwInit()の項目にこうありますね<br>>This function registers a function calling glfwTerminate with the atexit facility of the C library.<br>つまり、atexit()でterm()を登録するのはどちらかというと冗長な記述になっていて(たぶん)、このあたりが先生の感じている違和感に通じるのでは。<br><br>同マニュアルの glfwTerminate() の項目に<br>>This function should be called before a program exits.<br>とあるのは、quick_exit()とかabort()とかraise()とか_Exit()で終了する場合を想定しているのだと思います( User guide のプログラムでは二回呼ばれてますが……)

とこ 2012年09月18日 14:23

通りすがり様,ご指摘ありがとうございます!<br>User Guide のプログラムの方を見て,そういうことはしてないんじゃないかと思ってました.ぬかっておりました.<br>コメントにも気を遣っていただき,ありがとうございますw


編集 «(1) GLFW で OpenGL を使う 最新 (3) OpenGL のバージョンとプロファイルの指定»