必要性がありそうなのでOpenGLの勉強を始めましたという話です。
もともとOpenGLとかDirectXとか全く使ったことがないわけではなかったんですが、何かやりたいことがあるときにピンポイントでソースコードを拾ってきてなんとなく動かす程度で、頻繁に使うわけでもないため、すぐに忘れてしまうことを繰り返していました。
少し腰を据えて勉強して、記録も残しておきたいということで、少しOpenGLに関するコーディングをやっていきたいと思います。
本日は環境構築と簡単なプログラムの実装を行います。
・Windows 10
・Visual Studio 2015 Community(導入済)
環境構築に時間を掛けるのも面倒なので、サクっと環境構築したいです。Visual Studioでの開発であれば、まずVisual Studioでの開発を始めるのであればNugetで入れてしまうのが早そうです。
開発用のプロジェクトの作成を行います。
「Win32コンソールアプリケーション」を選択し、適当な場所にプロジェクトを作成します。
お好みではありますが、基本的に「空のプロジェクト」にチェックを入れるようにしています。
「Release/x64」に変更し、Source.cppを作りました。今回はこの.cppファイルの中にプログラムを書いていきます。
上部のタブから「プロジェクト」->「NuGetパッケージの管理」を選択します。
上部の参照タブより、検索欄に「nupengl」を入れて検索します。二つ出てくるので、nupengl.coreを入れましょう。選択して、右側の「インストール」ボタンでインストールが自動で開始されます。
途中ウィンドウが一つくらい出るかもしれませんがOKで進んでください。
最終的に以下のように「正常にインストールされました」が出れば完了です。
これでOpenGLの環境構築は完了しており、後はコーディングを進めていける体制が整いました。
今回作成したコードは以下となります。
真っ白なウィンドウが表示され、描画処理が行われる度にコンソールにメッセージが流れます。以下にそれぞれの部分を説明していきます。
まずはmain関数の上から辿っていきます。
GLUTとは「OpenGL Utility Toolkit」のことで、OpenGLのプログラムをOSの違い等を気にせずに簡単に書けるようにするライブラリです。
これを使うことで簡単にウィンドウ表示やマウス操作に対応させたりすることが可能です。
nupenglを入れると付いてきますので、「#include <GL/glut.h>」をインクルードして使います。
以下の関数で初期化されます。
基本的にはほぼほぼおまじないみたいに書いてしまえば良いのでは思います。
これから表示されるウィンドウに対する設定をします。対応するコードは以下の部分になります。
glutInitWindowSize(800, 600); // 画面サイズを指定する glutInitWindowPosition(100, 100); // 画面の初期位置を指定する glutInitDisplayMode(GLUT_RGBA); // 表示モード設定 glutCreateWindow("OpenGL Window"); // ウィンドウの名前
glutInitWindowSizeでウィンドウのサイズを、glutInitWindowPositionで起動したときのウィンドウの位置を、glutCreateWindowでウィンドウ名を設定します。
ここは上記の図のように、視覚的にもわかりやすいと思います。
この関数はウィンドウモードを規定しています。
GLUT_RGBA以外にもいくつかのモードが存在しています。
GLUT_RGBAは、これから表示するウィンドウはR(赤)、G(緑)、B(青)、A(透明度)の四つの要素で、各ピクセルの色が規定されていますよ、という設定です。
まずはカラーで何か図形等を表示させたりしたいと思っているので、ひとまずは上記の設定で良いでしょう。
glutDisplayFunc(display); // 描画処理が必要なときに呼ばれる
この関数の中身は以下です。
// 描画処理が必要なときに呼ばれる void display(void) { display_num++; std::cout << "display : " << display_num << "回目" << std::endl; glClear(GL_COLOR_BUFFER_BIT); // カラーバッファを初期化する glFlush(); }
OpenGLでは、実際の各フレームの処理が行われる前に、コールバック関数を設定します。
コールバック関数とは「何らかのイベントが発生したらこの関数を呼びますよ」という関数のことです。
特に3DCGなどをOpenGLで描画する場合、時間と共に立方体が回転して見えるようなプログラムを書くことを考えてみましょう。
このとき、立方体は回転するわけですから、フレームごとに立方体の見え方が変わり、画面に表示される2D表示も変化していくことになります。
これはすなわち、ものすごい頻度で描画処理を繰り返すことで、画面が毎回更新されていき、画面的に立方体が回転しているように見えるわけです。
他にも、例えば「キーボードの[Esc]キーが押されたら」とか「マウスがクリックされたら」とか、あるいは800×600のウィンドウを作ったものの、そのウィンドウを縮められてしまったら、とか、様々なイベントが起こる可能性があります。
そういったときに呼ばれるのがコールバック関数です。
今回のプログラムでは、描画処理が行われたときに「display」という関数を呼びますよということを示しています。
コールバック関数は、上で説明したように様々な種類があるので、少しずつ使えるものを増やしていきたいところです。
あくまで、このglutDisplayFunc()が書かれている位置でこの関数が実行されているわけではないことは注意してください。
ここまではウィンドウの設定や、描画処理をしたらこの関数を使うよ! という設定をしただけでした。
次に、画面の初期化を行います。
initialize(); // 初期化
void initialize(void) { glClearColor(1.0, 1.0, 1.0, 1.0); // 画面を白にする }
glClearColor関数は、カラーバッファの初期値を決定する関数です。
カラーバッファとは、今回画面の表示に使う配列だと考えればよいと思います。例えば、真っ白な画面を表示する場合、「[1][1][1][1] [1][1][1][1] [1][1][1][1] } … …」のように画素のRGBA値が連続で入り続け、それぞれのピクセルの色を示すようなイメージになります。
このようなバッファは他に深度値を入れるデプスバッファや、ステンシルバッファがありますが、今回は特に扱いません。
カラーバッファというバッファがあり、画面の色を表すのに使います。
この関数は、カラーバッファの初期値を決定します。
すなわち、カラーバッファの初期値は[1][1][1][1]です、と設定します。
ここで再度display関数を見てみます。
// 描画処理が必要なときに呼ばれる void display(void) { display_num++; std::cout << "display : " << display_num << "回目" << std::endl; glClear(GL_COLOR_BUFFER_BIT); // カラーバッファを初期化する glFlush(); }
この関数は、カラーバッファを事前に設定された初期値で埋め尽くします。よって、カラーバッファの中身が「[1][1][1][1] [1][1][1][1] [1][1][1][1] } … …」となり、真っ白になります。
コマンドバッファに蓄積された命令を実行する関数です。コマンドバッファは、命令を蓄積しておくバッファのことで、OpenGLでは命令は即座に実行されるわけではなく、一度コマンドバッファというところに蓄積されてから実行されます。
その実行命令を出すのが上記関数になります。
glutMainLoop();
上記関数が実行されると、イベントの実行待ちになります。
つまり、コールバック関数で予め仕掛けておいた関数が、イベントの発生と共に呼び出されるメインループに入ることになります。
しかし、そもそも描画処理の関数っていつ呼ばれているんでしょう?
それを疑問に思ったので、今回のコードではdisplay()関数が呼ばれると、コンソールに回数が出るような形にしました。
今回のプログラムを起動すると、真っ白なウインドウが表示されます。
この時点で、既に2回描画関数が呼ばれています。
ウィンドウをマウスでクリックした際にも描画関数が呼ばれています。
ウィンドウを動かしたり、
ウインドウのサイズを変更したりした場合には、以下のように描画関数が呼ばれ続けていました。
このように、描画のコールバック関数は、あらゆるアクションに反応して呼ばれるようです。このイメージは掴んでおいた方がいいでしょう。
今回はOpenGLの導入で簡単なプログラムを作成しました。
次回からは何らかの図形の表示等を行っていきたいと思います。