GLUTを使うと,様々なプラットフォーム上で動作するOpenGLのアプリケーションが簡単に書けます.
GHCには標準でこのライブラリが含まれるので,色々試してみようと思います.
GLUTを使ったプログラムの実行には,GLUTのライブラリが必要です.
インターネット上にたくさん情報があるので調べてください.
for WIN32
GLUTのコールバック関数の登録等には,$=演算子を使います.
$=演算子はHasSetterクラスのメソッドで,実際には StateVar型に対してよく使う気がします. この型はStateモナドと同じような物で,状態を持つ為に使います.
$=演算子はStateVar型の状態を更新する為に使います.
例えば
clearColor $= Color4 0.0 0.0 0.0 0.0
とすると,clear関数でColorBufferをクリアする時に
「Color4 0.0 0.0 0.0 0.0」でクリアされる事になります.
http://www.haskell.org/ghc/docs/latest/html/libraries/OpenGL/Graphics-Rendering-OpenGL-GL-Framebuffer.html#v%3Aclear
C言語が分かる方なら
GLUTの設定を保持する為の,StateVar型のグローバル変数(clearColor等)があって
その変数に値を代入する為に,$=演算子を使う.
と思っておいて,問題は無いと思います.
(色々なHaskellの偉い方に怒られそうですが^^;
四角形がぐるぐる回るサンプルプログラムです.
を試してみました.
import Graphics.UI.GLUT import Graphics.Rendering.OpenGL.GLU import Data.IORef --タイマの間隔 timerInterval = 40 main = do --回転の角度を初期化 rot <- newIORef 0.0 --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "guruGuru" --表示に使うコールバック関数の指定 displayCallback $= display rot --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just reshape --タイマを作る addTimerCallback timerInterval $ timerProc (display rot) --GLUTのメインループに入る mainLoop display rot = do --回転させる modifyIORef rot (+14.4) r <- readIORef rot --背景を黒にする clearColor $= Color4 0.0 0.0 0.0 0.0 clear [ColorBuffer] --単位行列を読み込む loadIdentity --表示 preservingMatrix $ do rotate r (Vector3 0.0 0.0 1.0 :: Vector3 GLdouble) renderPrimitive Quads $ mapM_ vertex [ Vertex3 0.10 0.10 0.0, Vertex3 (-0.10) 0.10 0.0, Vertex3 (-0.10) (-0.10) 0.0, Vertex3 0.10 (-0.10) 0.0 :: Vertex3 GLfloat] --バッファの入れ替え swapBuffers --タイマが呼ばれるたびにactを繰り返す timerProc act = do act addTimerCallback timerInterval $ timerProc act --ウィンドウのサイズが変更された時の処理 reshape size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 lookAt (Vertex3 0.0 0.0 (-1.0)) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0) matrixMode $= Modelview 0
main.hs等のファイル名で保存して
ghc -package GLUT main.hs
としてやれば実行ファイルができます.
回転の角度を格納するのに,今回はIORefモナドを使う事にしました.
モナドと聞くと難しいイメージがありますが,道具として簡単に使うだけなら
詳細な理論等は気にしないでOKです.これを使う為に
import Data.IORef
としてモジュールを読み込みます.
このIORefを使うと,C++で言う所の参照を扱う事ができます.例えば
ref <- newIORef 1
とすると,refには1を持つ変数への参照を返します.C++での
ref = new int(1);
に似ていますね.
refは参照なので,そのままでは整数として扱えません.
r <- readIORef ref
のようにすると,整数を取り出す事ができます.
このIORefには他にもwriteIORefやmodifyIORef等の関数が使えます.使い方はソースコードを読めば分かると思います.
キーボードの動きに応じて動作を変える事ができれば,時間とやる気しだいで何でも作れますね(本当かな?^^;).
先ほどのプログラムに少しコードを書き足して,キーボードの入力で回転の方向が変わるようにしてみます.
import Graphics.UI.GLUT import Graphics.Rendering.OpenGL.GLU import System import Data.IORef --タイマの間隔 timerInterval = 40 main = do --回転の角度を初期化 rot <- newIORef 0.0 arg <- newIORef 14.4 --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "guruGuru" --表示に使うコールバック関数の指定 displayCallback $= display rot arg --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just reshape --キーボードやマウスのコールバック keyboardMouseCallback $= Just (keyboardProc arg) --タイマを作る addTimerCallback timerInterval $ timerProc (display rot arg) --GLUTのメインループに入る mainLoop display rot arg= do --回転させる w<-readIORef arg --w <- readIORef hoge modifyIORef rot (+w) r <- readIORef rot --背景を黒にする clearColor $= Color4 0.0 0.0 0.0 0.0 clear [ColorBuffer] --単位行列を読み込む loadIdentity --表示 preservingMatrix $ do rotate r (Vector3 0.0 0.0 1.0 :: Vector3 GLdouble) renderPrimitive Quads $ mapM_ vertex [ Vertex3 0.10 0.10 0.0, Vertex3 (-0.10) 0.10 0.0, Vertex3 (-0.10) (-0.10) 0.0, Vertex3 0.10 (-0.10) 0.0 :: Vertex3 GLfloat] --バッファの入れ替え swapBuffers --タイマが呼ばれるたびにactを繰り返す timerProc act = do act addTimerCallback timerInterval $ timerProc act --ウィンドウのサイズが変更された時の処理 reshape size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 lookAt (Vertex3 0.0 0.0 (-1.0)) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0) matrixMode $= Modelview 0 --キー入力の処理 keyboardProc arg ch state _ _ | ch == Char 'q' = exitWith ExitSuccess --qが押されたら終了 | state == Down = modifyIORef arg (*(-1)) --それ以外なら回転の方向を変える | otherwise = return ()
こんな感じです.先ほどと同じく
ghc -package GLUT main.hs
とすればコンパイルできます.
keyboardMouseCallback $= Just (keyboardProc arg)
これでキーボードやマウスにイベントが起こった時に呼ばれるコールバック関数を指定します.
argは一度に回転させる角度を格納した変数への参照です.
keyboardProc arg ch state _ _ | ch == Char 'q' = exitWith ExitSuccess --qが押されたら終了 | state == Down = modifyIORef arg (*(-1)) --それ以外なら回転の方向を変える | otherwise = return ()
このkeyboardProcがキーを押した時に呼ばれます.詳しい説明はこちらを読んでください.
chはキーの種類,stateに押したのか離したのかが格納されています.
ガード部で,qが押された時に終了,それ以外のキーが押された時に回転方向を反転するようにしました.
Haskellで乱数を扱ってみます.
左の点からランダムにパーティクルが飛びだします.
モナドの扱いにまだ慣れていないせいか,型チェックに引っかかって,なかなかコンパイルが通りませんでした^^;
module Main where import Graphics.UI.GLUT hiding (position) import Graphics.Rendering.OpenGL.GLU import System import System.Random import Data.IORef --タイマの間隔 timerInterval = 16 type Point= Vertex3 GLdouble data GameObject = Particle{position::Point, velocity::Point} data GameState = Game{objects::[GameObject]} data GameRenderer=Renderer{rendererFunc::DisplayCallback, game::IORef GameState} updateObject::GameObject->GameObject updateObject Particle{position=pos, velocity=vel} =Particle{ position = addVer3 pos vel, velocity = addVer3 vel (Vertex3 0.0 (-0.001) 0.0) } where addVer3 (Vertex3 x1 y1 z1) (Vertex3 x2 y2 z2) = Vertex3 (x1+x2) (y1+y2) (z1+z2) updateObjects::[GameObject]->[GameObject] updateObjects os = filter (\o-> getY (position o) >(-0.5)) --ある程度落ちた点は消す $ map updateObject os --座標を更新する where getY (Vertex3 _ y _) = y newParticle::IO GameObject newParticle=do newVel <- getRandomVel return Particle{ position=Vertex3 0 0 0, velocity=newVel} where getRandomVel::IO Point getRandomVel=do gx <- newStdGen (x,gy) <- return $ randomR ( 0.01, 0.02) gx (y,gz) <- return $ randomR ( 0.01, 0.03) gy (z,g') <- return (0.0, gz) -- $ randomR (-0.02, 0.02) gz return $ Vertex3 x y z newParticles::Int->IO [GameObject] newParticles 0 = return [] newParticles n = do p <- newParticle ps<- newParticles $ n-1 return $ p:ps main = do --Gameを作る p <- newParticle gameState <- newIORef Game{objects=[p]} gameRenderer <- newIORef Renderer{ rendererFunc=display gameState, game=gameState} --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "Particle" --表示に使うコールバック関数の指定 displayCallback $= display gameState-- gameState --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just reshape --キーボードやマウスのコールバック keyboardMouseCallback $= Just (keyboardProc) --タイマを作る addTimerCallback timerInterval $ timerProc gameRenderer --GLUTのメインループに入る mainLoop display gameState= do --背景を黒にする clearColor $= Color4 0.0 0.0 0.0 0.0 clear [ColorBuffer] --単位行列を読み込む loadIdentity gs <- readIORef gameState pointSize $=4.0 --表示 preservingMatrix $ do renderPrimitive Points $ mapM_ vertex$ map position (objects gs) --バッファの入れ替え swapBuffers --タイマが呼ばれるたびにactを繰り返す timerProc::IORef GameRenderer->IO () timerProc grRef = do gr <- readIORef grRef gs <- readIORef $ game gr rendererFunc gr ps <-newParticles 32 writeIORef (game gr) Game{ objects=ps++(updateObjects$objects gs)} addTimerCallback timerInterval $ timerProc grRef --ウィンドウのサイズが変更された時の処理 reshape size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 lookAt (Vertex3 0.5 0.2 (1.0)) (Vertex3 0.5 0.2 0.0) (Vector3 0.0 1.0 0.0) matrixMode $= Modelview 0 --キー入力の処理 keyboardProc ch state _ _ | ch == Char 'q' = exitWith ExitSuccess --qが押されたら終了 -- | state == Down = modifyIORef arg (*(-1)) --それ以外なら回転の方向を変える | otherwise = return ()
GLUT的な変更点はありません.
指定した範囲の乱数を取得する関数randomRを使ってみました.
使い方はここが参考になると思います.
矢印キーで,パーティクルを飛ばしながら飛行する物体を操作できます.
今回もHaskell的で無いです.
module Main where import Graphics.UI.GLUT hiding (position) import Graphics.Rendering.OpenGL.GLU import System import System.Random import Data.IORef import Control.Monad import qualified Data.Set as Set import qualified Data.Map as Map --タイマの間隔 timerInterval = 16 type Point= Vertex3 GLdouble data GameObject = Particle{position::Point, velocity::Point} | Player{position::Point, velocity::Point} data GameState = Game{ player::GameObject, objects::[GameObject], keys::IORef (Set.Set Key)} data GameRenderer=Renderer{rendererFunc::DisplayCallback, game::IORef GameState} addVer3::Num a => Vertex3 a->Vertex3 a->Vertex3 a addVer3 (Vertex3 x1 y1 z1) (Vertex3 x2 y2 z2) = Vertex3 (x1+x2) (y1+y2) (z1+z2) subVer3::Num a => Vertex3 a->Vertex3 a->Vertex3 a subVer3 (Vertex3 x1 y1 z1) (Vertex3 x2 y2 z2) = Vertex3 (x1-x2) (y1-y2) (z1-z2) updateObject::GameObject->GameObject updateObject Particle{position=pos, velocity=vel} =Particle{ position = addVer3 pos vel, velocity = vel}-- 重力ON addVer3 vel (Vertex3 0.0 (-0.001) 0.0) } updateObjects::[GameObject]->[GameObject] updateObjects os = map updateObject os -- filter (\o-> getY (position o) >(-0.5)) --ある程度落ちた点は消す -- $ map updateObject os --座標を更新する where getY (Vertex3 _ y _) = y newParticle::Point->Point->IO GameObject newParticle p v=do newVel <- getRandomVel return Particle{ position=p, velocity=addVer3 v newVel} where getRandomVel=do gx <- newStdGen (x,gy) <- return $ randomR ( -0.003, 0.003) gx (y,gz) <- return $ randomR ( -0.003, 0.003) gy (z,g') <- return $ randomR ( -0.003, 0.003) gz return $ Vertex3 x y z --個数 場所 方向 newParticles::Int->Point->Point->IO [GameObject] newParticles 0 _ _ = return [] newParticles n p v= do new <- newParticle p v ps<- newParticles (n-1) p v return (new:ps) main = do --Gameを作る keyState <- newIORef Set.empty gameState <- newIORef Game{ player=Player{position=Vertex3 0 0 0, velocity=Vertex3 0 0 0}, objects=[], keys=keyState} gameRenderer <- newIORef Renderer{ rendererFunc=display gameState, game=gameState} --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "Particle" --表示に使うコールバック関数の指定 displayCallback $= display gameState-- gameState --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just (reshape gameState) --キーボードやマウスのコールバック keyboardMouseCallback $= Just (keyboardProc keyState) --タイマを作る addTimerCallback timerInterval $ timerProc gameRenderer --GLUTのメインループに入る mainLoop display gameState= do --背景を黒にする clearColor $= Color4 0.0 0.0 0.0 0.0 clear [ColorBuffer] --単位行列を読み込む loadIdentity gs <- readIORef gameState setCenter gs pointSize $=4.0 --表示 showParticles $ objects gs showPlayer $ player gs --バッファの入れ替え swapBuffers where showParticles os = preservingMatrix $ do renderPrimitive Points $ mapM_ vertex$ map position os showPlayer Player{position=Vertex3 x y z, velocity=_} = preservingMatrix $ do translate (Vector3 x y z) renderObject Wireframe (Sphere' 0.05 5 5)--(Teapot 0.06) modifyGame::IORef GameState->IO() modifyGame game = do gs <- readIORef game ks <- readIORef $ keys gs --プレイヤーを動かす ppos <- return $ position $ player gs pvel <- return $ velocity $ player gs addVecList <- return $ map (`searchMap` vectorMap) $ Set.elems ks addVec <- return $ foldl (addVer3) (Vertex3 0 0 0) addVecList --プレイヤーが動くならパーティクルを吐く Vertex3 x y z <- return addVec ps <-if or [null addVecList, addVec==Vertex3 0 0 0] then return [] else newParticles 32 (addVer3 ppos pvel) (addVer3 pvel (Vertex3 (-x) (-y) (-z))) newPlayer <- return Player{ position=addVer3 ppos pvel, velocity=addVer3 pvel addVec } writeIORef game Game{ player =newPlayer, objects =take 10240 $ ps++(updateObjects$objects gs), keys =keys gs} where searchMap f ((ch,v):xs)= if f==ch then v else searchMap f xs searchMap f []=Vertex3 0 0 0 vectorMap = [ (SpecialKey KeyLeft, Vertex3 (-0.003) 0 0), (SpecialKey KeyRight, Vertex3 0.003 0 0), (SpecialKey KeyUp, Vertex3 0 0.003 0), (SpecialKey KeyDown, Vertex3 0 (-0.003) 0)] timerProc::IORef GameRenderer->IO () timerProc grRef = do gr <- readIORef grRef modifyGame $ game gr gs <- readIORef $ game gr rendererFunc gr addTimerCallback timerInterval $ timerProc grRef setCenter gs = do Vertex3 x y z <- return $ position$player gs lookAt (Vertex3 0.0 0.0 (1.0)) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0) --lookAt (Vertex3 x y (1.0)) (Vertex3 x y 0.0) (Vector3 0.0 1.0 0.0) --ウィンドウのサイズが変更された時の処理 reshape gameState size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う gs <- readIORef gameState --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 setCenter gs matrixMode $= Modelview 0 --キー入力の処理 keyboardProc keySet ch state _ _ | ch == Char 'q' = exitWith ExitSuccess --qが押されたら終了 | state == Down = modifyIORef keySet (Set.insert ch) | state == Up = modifyIORef keySet (Set.delete ch) | otherwise = return ()
GLUT的な変更点はありません.
IORefなSetを使ってキーの状態を覚えています.この辺り
| state == Down = modifyIORef keySet (Set.insert ch) | state == Up = modifyIORef keySet (Set.delete ch)
半透明に合成できるとかっこいいエフェクトを作る事ができます.
今まではパーティクルの表示にPointsを使っていましたが,私の使っているノートPCがPointSpritesに未対応だった為
今後の事を考え,今回から3DSprite(ビルボード)を利用しています*1.
module Main where import Graphics.UI.GLUT hiding (position) import Graphics.Rendering.OpenGL.GLU import System import System.Random import Data.IORef import Control.Monad import qualified Data.Set as Set import qualified Data.Map as Map --タイマの間隔 timerInterval = 16 type Point= Vertex3 GLdouble data GameObject = Particle{position::Point, velocity::Point, temperature::Float} | Player{position::Point, velocity::Point} data GameState = Game{ player::GameObject, objects::[GameObject], keys::IORef (Set.Set Key)} data GameRenderer=Renderer{rendererFunc::DisplayCallback, game::IORef GameState} addVer3::Num a => Vertex3 a->Vertex3 a->Vertex3 a addVer3 (Vertex3 x1 y1 z1) (Vertex3 x2 y2 z2) = Vertex3 (x1+x2) (y1+y2) (z1+z2) subVer3::Num a => Vertex3 a->Vertex3 a->Vertex3 a subVer3 (Vertex3 x1 y1 z1) (Vertex3 x2 y2 z2) = Vertex3 (x1-x2) (y1-y2) (z1-z2) updateObject::GameObject->GameObject updateObject Particle{position=pos, velocity=vel, temperature=temp} =Particle{ position = addVer3 pos vel, velocity = vel, temperature=temp-1} updateObjects::[GameObject]->[GameObject] updateObjects [] = [] updateObjects (o:os) =case o of Particle{position=_, velocity=_, temperature=_} -> if temperature o > 0 then updateObject o:(updateObjects os) else updateObjects os otherwise -> updateObjects os where getY (Vertex3 _ y _) = y tempAve=50 newParticle::Point->Point->IO GameObject newParticle p v=do newVel <- getRandomVel newTemp<- getRandomTemp return Particle{ position=p, velocity=addVer3 v newVel, temperature=newTemp} where getRandomTemp=do g <- newStdGen (t,g') <- return $ randomR (tempAve-10, tempAve+10) g return t getRandomVel=do gx <- newStdGen (x,gy) <- return $ randomR ( -0.006, 0.006) gx (y,gz) <- return $ randomR ( -0.006, 0.006) gy (z,g') <- return $ randomR ( -0.006, 0.006) gz return $ Vertex3 x y z --個数 場所 方向 newParticles::Int->Point->Point->IO [GameObject] newParticles 0 _ _ = return [] newParticles n p v= do new <- newParticle p v ps<- newParticles (n-1) p v return (new:ps) main = do --Gameを作る keyState <- newIORef Set.empty gameState <- newIORef Game{ player=Player{position=Vertex3 0 0 0, velocity=Vertex3 0 0 0}, objects=[], keys=keyState} gameRenderer <- newIORef Renderer{ rendererFunc=display gameState, game=gameState} --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "Particle" --表示に使うコールバック関数の指定 displayCallback $= display gameState-- gameState --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just (reshape gameState) --キーボードやマウスのコールバック keyboardMouseCallback $= Just (keyboardProc keyState) --タイマを作る addTimerCallback timerInterval $ timerProc gameRenderer --GLUTのメインループに入る mainLoop display gameState= do --背景を黒にする clearColor $= Color4 0.0 0.0 0.0 0.0 clear [ColorBuffer] blend$=Enabled blendFunc $= (SrcAlpha, OneMinusSrcColor) --単位行列を読み込む loadIdentity gs <- readIORef gameState setCenter gs pointSize $=4.0 --表示 showParticles (objects gs) showPlayer $ player gs --バッファの入れ替え swapBuffers where tempToColor4 t a = Color4 (1.0-0.02*(tempAve-t)) (1.0-0.04*(tempAve-t)) (1.0-0.08*(tempAve-t)) a getSprite w=[ Vertex3 (-w) (-w) 0, Vertex3 (-w) w 0, Vertex3 w (-w) 0, Vertex3 w (-w) 0, Vertex3 (-w) w 0, Vertex3 w w 0] showParticles []= return () showParticles (o:os) = do color (tempToColor4 (temperature o) 0.8) preservingMatrix $ renderPrimitive Triangles $ do mapM_ vertex $ map (addVer3 $ position o) $ getSprite 0.04 showParticles os showPlayer Player{position=Vertex3 x y z, velocity=_} = do color $ Color3 (1.0::Double) 1.0 1.0 preservingMatrix $ do translate (Vector3 x y z) renderObject Wireframe (Sphere' 0.05 5 5)--(Teapot 0.06) modifyGame::IORef GameState->IO() modifyGame game = do gs <- readIORef game ks <- readIORef $ keys gs --プレイヤーを動かす ppos <- return $ position $ player gs pvel <- return $ velocity $ player gs addVecList <- return $ map (`searchMap` vectorMap) $ Set.elems ks addVec <- return $ foldl (addVer3) (Vertex3 0 0 0) addVecList --プレイヤーが動くならパーティクルを吐く Vertex3 x y z <- return addVec ps <-if or [null addVecList, addVec==Vertex3 0 0 0] then return [] else newParticles 8 (addVer3 ppos pvel) (addVer3 pvel (Vertex3 (-x) (-y) (-z))) newPlayer <- return Player{ position=addVer3 ppos pvel, velocity=addVer3 pvel addVec } writeIORef game Game{ player =newPlayer, objects =ps++(updateObjects$objects gs), keys =keys gs} where searchMap f ((ch,v):xs)= if f==ch then v else searchMap f xs searchMap f []=Vertex3 0 0 0 vectorMap = [ (SpecialKey KeyLeft, Vertex3 (-0.003) 0 0), (SpecialKey KeyRight, Vertex3 0.003 0 0), (SpecialKey KeyUp, Vertex3 0 0.003 0), (SpecialKey KeyDown, Vertex3 0 (-0.003) 0)] timerProc::IORef GameRenderer->IO () timerProc grRef = do gr <- readIORef grRef modifyGame $ game gr gs <- readIORef $ game gr rendererFunc gr addTimerCallback timerInterval $ timerProc grRef setCenter gs = do Vertex3 x y z <- return $ position$player gs --lookAt (Vertex3 0.0 0.0 (1.0)) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0) lookAt (Vertex3 x y (1.0)) (Vertex3 x y 0.0) (Vector3 0.0 1.0 0.0) --ウィンドウのサイズが変更された時の処理 reshape gameState size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う gs <- readIORef gameState --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 setCenter gs matrixMode $= Modelview 0 --キー入力の処理 keyboardProc keySet ch state _ _ | ch == Char 'q' = exitWith ExitSuccess --qが押されたら終了 | state == Down = modifyIORef keySet (Set.insert ch) | state == Up = modifyIORef keySet (Set.delete ch) | otherwise = return ()
アルファブレンドを有効にする為に
blend$=Enabled
アルファブレンドの仕方を
blendFunc $= (SrcAlpha, OneMinusSrcColor)
のように指定します.
後は色を指定する際にColor4を使って,4番目の色の要素に透明度を指定します(0が透明,1.0が不透明)
color (Color4 1.0 0 0 0.5)
詳しい使い方は,OpenGLの説明をしたページでglBlendFunc関数について調べるとよいでしょう.
GLUTではビットマップフォントとストロークフォントによる文字の描画をサポートしていますが
Haskellからもこの機能が利用して文字を描く事ができます.
import Graphics.UI.GLUT import Graphics.Rendering.OpenGL.GLU main = do --GLUTの初期化 initialDisplayMode $= [RGBAMode, DoubleBuffered] initialWindowSize $= Size 640 480 --ウィンドウを作る createWindow "renderString sample" --表示に使うコールバック関数の指定 displayCallback $= display --ウィンドウのサイズが変更された時に呼ぶコールバック関数の指定 reshapeCallback $= Just reshape --GLUTのメインループに入る mainLoop display = do --背景を青にする clearColor $= Color4 0.0 0.0 1.0 0.0 clear [ColorBuffer] --単位行列を読み込む loadIdentity --表示 lineWidth $= 4.0 preservingMatrix $ do scale (0.001::Double) 0.001 0.001 w <- stringWidth Roman "Stroke font" translate (Vector3 (-0.5*(fromIntegral w)) 0 0 ::Vector3 Float) renderString Roman "Stroke font" --バッファの入れ替え swapBuffers --ウィンドウのサイズが変更された時の処理 reshape size@(Size w h)=do viewport $= (Position 0 0, size) --ウィンドウ全体を使う --ビューボリュームの設定 matrixMode $= Projection loadIdentity perspective 60.0 (fromIntegral w / fromIntegral h) 0.001 50.0 --少し後ろから撮影 lookAt (Vertex3 0.0 0.0 1.0) (Vertex3 0.0 0.0 0.0) (Vector3 0.0 1.0 0.0) matrixMode $= Modelview 0
ビットマップフォントは思った動作をさせる事ができなかったので使っていません.
ストロークフォントの描画には,renderStringを使います.
このストロークフォントは,Lineとして書かれているので
translateやscale,lineWidth等の関数を適用できます.
また,stringWidthを使うことで,文字列を表示した時の幅を求める事もできます.