使用Haskell渲染一个OpenGL场景,我使用以下结构:
data Context = Context
{
contextRot1 :: IORef GLfloat
, contextRot2 :: IORef GLfloat
, contextRot3 :: IORef GLfloat
, contextZoom :: IORef Double
, contextTriangles :: IORef Triangles
}
Triangles
对象包含要显示的 3D 对象的顶点和法线,排列在三元组列表中形成三角形。
我在 main
函数中使用 reshapeCallback
()并与 Just (resize 0)
一起使用:
resize :: Double -> Size -> IO ()
resize zoom s@(Size w h) = do
viewport $= (Position 0 0, s)
matrixMode $= Projection
loadIdentity
perspective 45.0 (w'/h') 1.0 100.0
lookAt (Vertex3 0 (-9 + zoom) 0) (Vertex3 0 0 0) (Vector3 0 0 1)
matrixMode $= Modelview 0
where
w' = realToFrac w
h' = realToFrac h
然后我使用这个displayCallback
:
display :: Context -> DisplayCallback
display context = do
clear [ColorBuffer, DepthBuffer]
r1 <- get (contextRot1 context)
r2 <- get (contextRot2 context)
r3 <- get (contextRot3 context)
triangles <- get (contextTriangles context)
zoom <- get (contextZoom context)
(_, size) <- get viewport
loadIdentity
resize zoom size
rotate r1 $ Vector3 1 0 0
rotate r2 $ Vector3 0 1 0
rotate r3 $ Vector3 0 0 1
renderPrimitive Triangles $ mapM_ drawTriangle triangles
swapBuffers
where
drawTriangle ((v1, v2, v3), (n1, n2, n3)) = do
materialDiffuse Front $= whitesmoke
normal (toNormal n1)
vertex (toVertex v1)
normal (toNormal n2)
vertex (toVertex v2)
normal (toNormal n3)
vertex (toVertex v3)
where
toNormal (x, y, z) = Normal3 x y z
toVertex (x, y, z) = Vertex3 x y z
以下是 main
函数的代码:
main :: IO ()
main = do
_ <- getArgsAndInitialize
_ <- createWindow "Kohn-Nirenberg surface"
windowSize $= Size 512 512
initialDisplayMode $= [RGBAMode, DoubleBuffered, WithDepthBuffer]
clearColor $= discord
materialAmbient Front $= white
lighting $= Enabled
lightModelTwoSide $= Enabled
light (Light 0) $= Enabled
position (Light 0) $= Vertex4 0 (-100) 0 1
ambient (Light 0) $= black
diffuse (Light 0) $= white
specular (Light 0) $= white
depthFunc $= Just Less
shadeModel $= Smooth
cullFace $= Just Back
rot1 <- newIORef 0.0
rot2 <- newIORef 0.0
rot3 <- newIORef 0.0
zoom <- newIORef 0.0
triangles <- newIORef =<< trianglesIO
displayCallback $= display Context {contextRot1 = rot1,
contextRot2 = rot2,
contextRot3 = rot3,
contextZoom = zoom,
contextTriangles = triangles}
reshapeCallback $= Just (resize 0)
anim <- newIORef False
delay <- newIORef 0
save <- newIORef False
snapshots <- newIORef 0
keyboardCallback $= Just (keyboard rot1 rot2 rot3 zoom anim delay save)
idleCallback $= Just (idle anim delay save snapshots rot3)
putStrLn "*** Kohn-Nirenberg surface ***\n\
\ To quit, press q.\n\
\ Scene rotation:\n\
\ e, r, t, y, u, i\n\
\ Zoom: l, m\n\
\ Animation: a\n\
\ Animation speed: o, p\n\
\ Save animation: s\n\
\"
mainLoop
我没有展示所有的代码,因为它太长了,而且有些部分与当前问题无关(例如保存动画)。如果需要,您可以在这里找到完整的代码。
现在,由于keyboardCallback
(此处未显示),我可以旋转场景。我认为这会旋转3D对象,而不是相机。是这样吗?
事实上,旋转会消耗大量资源(当连续按下旋转键时,我可以听到笔记本电脑发出巨大的噪音)。
然而,当我使用带有R软件包rgl的OpenGL时,我可以用鼠标平稳地旋转场景,这根本不会消耗资源。因此,我想知道我在Haskell中使用的方法是否可以改进。我不知道rgl如何执行旋转。
编辑
注1:在此示例中,三角形并不需要使用IORef
。
注2:即使我没有按任何按键,只是观看场景,笔记本电脑也会发热;在我看来,main
函数在不断执行,即使什么也没有改变-难道没有一种方法可以控制它的重新执行吗?
mainloop
定义在哪里?我在代码库中找不到它。 - lsmormainloop
是来自于OpenGL
库或者GLUT
,我不确定。 - Stéphane Laurent