gpt4 book ai didi

haskell - 如何在Haskell中旋转OpenGL图形而又无用地重新评估图形对象?

转载 作者:行者123 更新时间:2023-12-02 02:55:13 26 4
gpt4 key购买 nike

为简化现实,我的OpenGL程序具有以下结构:

  • 开头有一个函数f : (Double,Double,Double) -> Double
  • 然后有一个函数triangulize :: ((Double,Double,Double) -> Double) -> [Triangle],以便triangulize f计算表面f(x,y,z)=0的三角形网格。
  • 然后是displayCallback,这是一个display :: IORef Float -> DisplayCallBack函数,用于显示图形(也就是说,它显示三角形网格)。这里的第一个参数IORef Float用于旋转图形,由于稍后定义的keyboardCallback,当用户按下键盘上的某个键时,其值(旋转角度)会更改。别忘了display函数调用triangulize f
  • 然后是以下问题。当用户按下键来旋转图形时,display函数被触发。然后重新评估triangulize f,而无需重新评估:旋转图形不会更改三角形网格(即triangulize f的结果与之前相同)。

  • 因此,有没有一种方法可以通过按一个键来旋转图形而不触发 triangulize f?换句话说,要“冻结” triangulize f,使其仅被评估一次,而不会被重新评估,这很耗时,但是却没有用,因为无论如何结果都是一样的。

    我相信这是在Haskell OpenGL中旋转图形的标准方法(我在某些tuto中以这种方式查看),因此我认为不必发布我的代码。但是我当然可以在需要时发布它。

    由于存在其他 IORef来控制表面的某些参数,因此实际情况更加复杂。但是,我首先想知道一些针对这种简化情况的解决方案。

    编辑:更多详细信息和一些代码

    简化代码

    所以,如果我按照上面的简化描述,我的程序看起来像
    fBretzel5 :: (Double,Double,Double) -> Double
    fBretzel5 (x,y,z) = ((x*x+y*y/4-1)*(x*x/4+y*y-1))^2 + z*z

    triangles :: [Triangle] -- Triangle: triplet of 3 vertices
    triangles =
    triangulize fBretzel5 ((-2.5,2.5),(-2.5,2.5),(-0.5,0.5))
    -- "triangulize f (xbounds, ybounds, zbounds)"
    -- calculates a triangular mesh of the surface f(x,y,z)=0

    display :: IORef Float -> DisplayCallback
    display rot = do
    clear [ColorBuffer, DepthBuffer]
    rot' <- get rot
    loadIdentity
    rotate rot $ Vector3 1 0 0
    renderPrimitive Triangles $ do
    materialDiffuse FrontAndBack $= red
    mapM_ drawTriangle triangles
    swapBuffers
    where
    drawTriangle (v1,v2,v3) = do
    triangleNormal (v1,v2,v3) -- the normal of the triangle
    vertex v1
    vertex v2
    vertex v3

    keyboard :: IORef Float -- rotation angle
    -> KeyboardCallback
    keyboard rot c _ = do
    case c of
    'e' -> rot $~! subtract 2
    'r' -> rot $~! (+ 2)
    'q' -> leaveMainLoop
    _ -> return ()
    postRedisplay Nothing

    这会导致上述问题。每次按下 'e''r'键时, triangulize函数将运行,而其输出保持不变。

    真实代码(几乎)

    现在,这是我的程序最接近实际的版本。实际上,它会为表面 f(x,y,z)=l计算一个三角形网格,在该网格中可以使用键盘更改“isolevel” l
    voxel :: IO Voxel
    voxel = makeVoxel fBretzel5 ((-2.5,2.5),(-2.5,2.5),(-0.5,0.5))
    -- the voxel is a 3D-array of points; each entry of the array is
    -- the value of the function at this point
    -- !! the voxel should never changes throughout the program !!

    trianglesBretz :: Double -> IO [Triangle]
    trianglesBretz level = do
    vxl <- voxel
    computeContour3d vxl level
    -- "computeContour3d vxl level" calculates a triangular mesh
    -- of the surface f(x,y,z)=level

    display :: IORef Float -> IORef Float -> DisplayCallback
    display rot level = do
    clear [ColorBuffer, DepthBuffer]
    rot' <- get rot
    level' <- get level
    triangles <- trianglesBretz level'
    loadIdentity
    rotate rot $ Vector3 1 0 0
    renderPrimitive Triangles $ do
    materialDiffuse FrontAndBack $= red
    mapM_ drawTriangle triangles
    swapBuffers
    where
    drawTriangle (v1,v2,v3) = do
    triangleNormal (v1,v2,v3) -- the normal of the triangle
    vertex v1
    vertex v2
    vertex v3

    keyboard :: IORef Float -- rotation angle
    -> IORef Double -- isolevel
    -> KeyboardCallback
    keyboard rot level c _ = do
    case c of
    'e' -> rot $~! subtract 2
    'r' -> rot $~! (+ 2)
    'h' -> level $~! (+ 0.1)
    'n' -> level $~! subtract 0.1
    'q' -> leaveMainLoop
    _ -> return ()
    postRedisplay Nothing

    解决方案的一部分

    实际上,我已经找到一种解决方案以“冻结”体素:
    voxel :: Voxel
    {-# NOINLINE voxel #-}
    voxel = unsafePerformIO $ makeVoxel fBretzel5 ((-2.5,2.5),(-2.5,2.5),(-0.5,0.5))

    trianglesBretz :: Double -> IO [Triangle]
    trianglesBretz level =
    computeContour3d voxel level

    这样,我认为 voxel永远不会重新评估。

    但是仍然存在问题。当 IORef rot更改时,要旋转图形,则没有理由重新评估 trianglesBretz:无论旋转什么, f(x,y,z)=level的三角形网格始终是相同的。

    因此,我如何对 display函数说:“嘿!当 rot更改时,不要重新评估 trianglesBretz,因为您会发现相同的结果”?

    我不知道如何像 NOINLINE一样将 trianglesBretz用于 voxel。除非 trianglesBretz level更改,否则将“冻结” level的内容。

    这是5孔椒盐脆饼:

    enter image description here

    编辑:基于@PetrPudlák的答案的解决方案。

    在@PetrPudlák很好的回答之后,我来到了下面的代码。我在这里给出此解决方案,以便将答案更多地放在 OpenGL的上下文中。
    data Context = Context
    {
    contextRotation :: IORef Float
    , contextTriangles :: IORef [Triangle]
    }

    red :: Color4 GLfloat
    red = Color4 1 0 0 1

    fBretz :: XYZ -> Double
    fBretz (x,y,z) = ((x2+y2/4-1)*(x2/4+y2-1))^2 + z*z
    where
    x2 = x*x
    y2 = y*y

    voxel :: Voxel
    {-# NOINLINE voxel #-}
    voxel = unsafePerformIO $ makeVoxel fBretz ((-2.5,2.5),(-2.5,2.5),(-1,1))

    trianglesBretz :: Double -> IO [Triangle]
    trianglesBretz level = computeContour3d voxel level

    display :: Context -> DisplayCallback
    display context = do
    clear [ColorBuffer, DepthBuffer]
    rot <- get (contextRotation context)
    triangles <- get (contextTriangles context)
    loadIdentity
    rotate rot $ Vector3 1 0 0
    renderPrimitive Triangles $ do
    materialDiffuse FrontAndBack $= red
    mapM_ drawTriangle triangles
    swapBuffers
    where
    drawTriangle (v1,v2,v3) = do
    triangleNormal (v1,v2,v3) -- the normal of the triangle
    vertex v1
    vertex v2
    vertex v3

    keyboard :: IORef Float -- rotation angle
    -> IORef Double -- isolevel
    -> IORef [Triangle] -- triangular mesh
    -> KeyboardCallback
    keyboard rot level trianglesRef c _ = do
    case c of
    'e' -> rot $~! subtract 2
    'r' -> rot $~! (+ 2)
    'h' -> do
    l $~! (+ 0.1)
    l' <- get l
    triangles <- trianglesBretz l'
    writeIORef trianglesRef triangles
    'n' -> do
    l $~! (- 0.1)
    l' <- get l
    triangles <- trianglesBretz l'
    writeIORef trianglesRef triangles
    'q' -> leaveMainLoop
    _ -> return ()
    postRedisplay Nothing

    main :: IO ()
    main = do
    _ <- getArgsAndInitialize
    _ <- createWindow "Bretzel"
    windowSize $= Size 500 500
    initialDisplayMode $= [RGBAMode, DoubleBuffered, WithDepthBuffer]
    clearColor $= white
    materialAmbient FrontAndBack $= black
    lighting $= Enabled
    lightModelTwoSide $= Enabled
    light (Light 0) $= Enabled
    position (Light 0) $= Vertex4 0 0 (-100) 1
    ambient (Light 0) $= black
    diffuse (Light 0) $= white
    specular (Light 0) $= white
    depthFunc $= Just Less
    shadeModel $= Smooth
    rot <- newIORef 0.0
    level <- newIORef 0.1
    triangles <- trianglesBretz 0.1
    trianglesRef <- newIORef triangles
    displayCallback $= display Context {contextRotation = rot,
    contextTriangles = trianglesRef}
    reshapeCallback $= Just yourReshapeCallback
    keyboardCallback $= Just (keyboard rot level trianglesRef)
    idleCallback $= Nothing
    putStrLn "*** Bretzel ***\n\
    \ To quit, press q.\n\
    \ Scene rotation:\n\
    \ e, r, t, y, u, i\n\
    \ Increase/Decrease level: h, n\n\
    \"
    mainLoop

    现在,无需执行无用的计算,即可旋转我的bretzel。

    enter image description here

    最佳答案

    我对OpenGL不太熟悉,因此在详细了解代码方面有些困难-如果我误解了一些内容,请纠正我。

    我会尽量避免使用不安全的函数或尽可能多地依赖INLINE。这通常会使代码变脆,并掩盖更自然的解决方案。

    在最简单的情况下,如果您不需要重新评估triangularize,我们可以将其替换为输出。所以我们有

    data Context = Context
    { contextRotation :: IORef Float,
    , contextTriangles :: [Triangle]
    }

    接着
    display :: Context -> DisplayCallback

    根本不会重新评估三角形,创建 Context时,它们只会被计算一次。

    现在,如果有两个参数,旋转和水平,则三角形取决于水平,而不取决于旋转:这里的技巧是正确管理依赖关系。现在,我们显式公开了参数的存储( IORef Float),因此,我们无法监视内部值何时更改。但是,调用者不需要知道如何存储参数的表示形式。它只需要以某种方式存储它们。因此,让我们有
    data Context = Context
    { contextRotation :: IORef Float,
    , contextTriangles :: IORef [Triangle]
    }


    setLevel :: Context -> Float -> IO ()

    也就是说,我们公开了一个存储参数的函数,但隐藏了内部结构。现在我们可以将其实现为:
    setLevel (Context _ trianglesRef) level = do
    let newTriangles = ... -- compute the new triangles
    writeIORef trianglesRef newTriangles

    由于三角形不取决于旋转参数,因此我们可以得到:
    setRotation :: Context -> Float -> IO ()
    setRoration (Context rotationRef _) = writeIORef rotationRef

    现在,依赖关系对于调用者是隐藏的。他们可以设置水平或旋转,而无需知道取决于它们的水平。同时,仅在需要时(级别更改)才更新三角形。 Haskell的惰性评估提供了一个不错的好处:如果在需要三角形之前水平发生多次更改,则不会对其进行评估。仅当 [Triangle]请求时,才会评估 IORef内部的 display thunk。

    关于haskell - 如何在Haskell中旋转OpenGL图形而又无用地重新评估图形对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49827020/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com