起動時のエラー処理
前回のプログラムでは 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 の代替品を探し始めたことがモチベーションなので、ターゲットは、しいて言えば自分です。ですが、一応、研究室の学生さんにも読んでもらおうと思ってるので、それなりに気をつけて書いています。そんな風にターゲットが揺らいでいるせいか、今回の文章は粒度がばらついている気がしますし、自分の流儀を押し付けてるところが目について、自分で読んでて気持ちが悪いです。ふぅ。