HaskellでGLUT使ってみた: ウインドウ表示・塗りつぶし
ちょっと気が早いかもですが、GUIの勉強をはじめました。
gtk2hsを使おうと考えていたのですが、どうもうまくインストールできず断念。
とりあえずGLUTを使ってみることに。こちらはcabal install GLUTで入りました。
しかし、GLUTそのものの資料はわりと見かけるのですが、Haskellで扱ってる人があまりいないようで調べづらかったです。
ここにプログラムの例がいくつか載ってたので参考になりました。
http://www.f13g.com/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0/Haskell/GLUT/
GLUTのCによる入門はよさげなページがありました。和歌山大学の講義ページのようですが……
http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html
とりあえず、ウインドウを出して中身を白く塗りつぶすHaskellプログラムはこうなります。上のページのCのプログラムをなるべく写経してみました。
各関数の詳細をわかるところまで書きます。正直GLUTもOpenGLも触るの初めてなんでよくわかってないところも多いですが……
Graphics.UI.GLUT.Initialization.getArgsAndInitialize :: IO (String, [String])
環境の初期化・オプション引数の処理を行い、余った(オプションなどではなく、使わなかった)引数及びプログラム名を返す関数です。
これか、またはinitialize関数などを呼んでおかないと、実行時に「GLUT・OpenGLの環境初期化がされてない」と怒られます。
とりあえずGLUT使うときには呼んでおくべき関数っぽいです。
Graphics.UI.GLUT.Window.createWindow :: String -> IO Window
ウインドウ名を受け取ってウインドウを生成し、ウインドウを識別するための値(Window型)を返す関数。
これを呼んだだけ(また、flushで実行しただけ)ではウインドウは表示されませんでした。
class HasSetter s where
($=) :: s a -> a -> IO ()
これはOpenGL・GLUT関連モジュールのものではなく、Data.StateVarモジュールの型クラス・および関数です。
書き込み可能な変数を表す型で、($=)メソッドにより状態の更新ができます。
GLUTでは、コールバック関数の設定や各状態変数のセットなどがこの関数を用いて行われているようです。
「(a型を書き込み可能な変数) $= a型」という形で登録します。どことなく手続き型っぽいインタフェースですね。
Graphics.UI.GLUT.Callbacks.Window.displayCallback :: SettableStateVar DisplayCallback
SettableStateVarはWrite-onlyな状態変数の型で、DisplayCallbackはIO ()型の型シノニムです。
current displayに対するコールバック(一連の処理みたいなもの?という認識。よくわかってないです)を登録するための変数です。
登録は上の$=演算子を使って行われます。
Graphics.UI.GLUT.Begin.mainLoop :: IO ()
GLUTのメインループに入り、GUIをイベント待ち受け状態にします。
一連の設定がすんだら最後に呼び出す関数です。
Graphics.Rendering.OpenGL.GL.Framebuffer.clearColor :: StateVar (Color4 GLclampf)
Graphics.Rendering.OpenGL.GL.Framebuffer.clear :: [ClearBuffer] -> IO ()
OpenGLはウインドウに対していくつかのバッファ(これもよくわからない。一つの画面に関する色などの情報を保持するメモリのようなもの?)を管理しており、HaskellではこれらをclearColor, clearIndex, clearDepth, clearStencilなどで設定することができ、設定後、clearを呼ぶことでcurrent windowに反映させることができるそうです。
clearColorに設定するColor4 GLclampf型は、
data Color4 a = Color4 !a !a !a !a
newtype GLclampf = GLclampf CFloat
のようになっており、指定する実数値の意味はそれぞれ "Red, Green, Blue, Alpha" で、値は0.0が最低・1.0が最大値らしいです。
Graphics.Rendering.OpenGL.GL.FlushFinish.flush :: IO ()
OpenGLは、命令をいくつか溜めこんでおき、一定量溜まった時点で実行するのですが、flushを実行すると、現在実行が保留されている命令が全て実行されます。
こんな感じでしょうか。ここまでやるだけでだいぶ調べました……
タイムラインを順次表示させることを当面の目標にしてますが、とりあえず今回はここまで。