簡體   English   中英

這個OpenGL Haskell代碼如何工作?

[英]How does this OpenGL Haskell code work?

我正在通過直接進入OpenGL學習Haskell,我似乎無法破譯這段代碼:

display :: DisplayCallback
display = do
  let color3f r g b = color $ Color3 r g (b :: GLfloat)
      vertex3f x y z = vertex $ Vertex3 x y (z :: GLfloat)

  clear [ColorBuffer]
  renderPrimitive Quads $ do
    color3f 1 0 0
    vertex3f 0 0 0
    vertex3f 0 0.2 0
    vertex3f 0.2 0.2 0
    vertex3f 0.2 0 0

    color3f 0 1 0
    vertex3f 0 0 0
    vertex3f 0 (-0.2) 0
    vertex3f 0.2 (-0.2) 0
    vertex3f 0.2 0 0

    color3f 0 0 1
    vertex3f 0 0 0
    vertex3f 0 (-0.2) 0
    vertex3f (-0.2) (-0.2) 0
    vertex3f (-0.2) 0 0

    color3f 1 0 1
    vertex3f 0 0 0
    vertex3f 0 0.2 0
    vertex3f (-0.2) 0.2 0
    vertex3f (-0.2) 0 0
  flush

到目前為止我的理解:

  1. display是一種功能。 問題:什么是DisplayCallback?
  2. do表示計算的鏈接,與IO monads有關
  3. color3fvertex3f是使用let關鍵字在do定義的三個參數的本地函數
  4. 我假設colorvertexglColor*glVertex*的openGL包裝函數。

現在這是令人困惑的地方:

  • Color3Vertex3似乎是由三個參數組成的某種數據結構。 問題:為什么有必要在這里使用數據結構? 為什么API的設計者選擇在這里使用它?

  • color3f函數調用color函數並傳入單個參數 - 數據結構Color3 ,數據為rg b。 由於某種原因,此處的數據類型僅為最后一個參數指定(b :: GLfloat) 問題:為什么僅為最后一個參數指定數據類型,為什么在此處指定它?

  • 問題:為什么在調用color函數color $ Color3 rg (b :: GLfloat)時使用美元符號?

繼續:

  1. clear是opengl glClear包裝器,並將列表作為參數,在這種情況下只包含一個元素[ColorBuffer]
  2. renderPrimitive似乎是opengl glBegin包裝函數, Quads似乎是數據類型。 問題:接下來會發生什么? $ do表示什么? (計算系列? 某事, IO monads?)。
  3. flushglFlush包裝器。

1。 DisplayCallbackIO ()的類型同義詞。 您可以使用hoogle查找內容,或在ghci中鍵入:i DisplayCallback

IO ()被賦予特殊名稱的原因是因為GLUT基於回調:注冊函數以處理特定事件,例如顯示,輸入事件,調整大小事件等。有關完整列表,請參閱文檔 顯然,您不需要類型同義詞,但它們是為了清晰和更好的通信而提供的。

2。 “我認為顏色和頂點是glColor *和glVertex *的openGL包裝函數。” 不完全 - OpenGL是更基本的OpenGLRaw的包裝器,它是c opengl庫到haskell的1:1映射。 vertexcolor稍微復雜一點glColorglVertex ,但你可以假設大多數用途它們是相同的。

更具體地說, vertexVertex類的成員,它有4個實例Vertex4Vertex3Vertex2

3。 Color3定義為data Color3 a = Color3 !a !a !a 感嘆號表明這些字段很嚴格。 為什么這有必要? 好吧,他們可以很容易地使用(a,a,a) -> IO ()a -> a -> a -> IO ()但是接受顏色的函數與采用矢量的函數無法區分,這是“在意識形態上“不同的對象,即使它們由完全相同的數據表示(頂點是data Vertex3 a = Vertex3 !a !a !a )。 因此,您不希望能夠將頂點傳遞給需要顏色的函數。 此外,由於嚴格性,這些數據類型在理想情況下提供了更好的性能。

4。 為什么指定類型? 簡短的回答,是類型系統需要它。 文字的類型是Num a => a ,它對於數據類型來說過於多態,這需要具體的類型。 因此,您使用類型注釋選擇所述具體類型。

為什么只在一個地方需要? 編譯器可以推斷其他字段的類型。 回顧數據類型decleration - 所有三個字段必須具有相同的類型。 如果一個字段被注釋,則其余字段被簡單推斷。 您還可以編寫Color3 r (g :: GLfloat) bColor3 (r :: GLfloat) gb或者為函數vertex3f提供類型簽名。

5。 $只是具有低優先級的函數應用程序,它被定義為f $ a = fa; infixr $ 0 f $ a = fa; infixr $ 0 你也可以寫color (Color3 rg (b :: GLfloat))所以這里純粹是一種風格問題。

6。 也許文檔將再次最好地解釋renderPrimitive正在做什么。 但是短版本是這樣的:不是在glBegin - glEnd塊中寫入內容,而是在renderPrimitive寫入它。 您不需要編寫glEnd因為它隱藏在renderPrimitive 所以你的代碼相當於:

glBegin (GL_QUADS); 
// All the stuff inside goes here ...
glEnd ();

OpenGL做了一些魔術,以便將c中的異常正確地引入haskell Universe,但你真的不必擔心這一點。

最后評論:如果您計划將haskell用於圖形,那么編寫實際的openGL代碼可能不是最好的主意。 畢竟,如果你打算使用openGL,為什么不用c? 那里有無數的圖形庫。 您應該瀏覽hackage以獲得適合您的包裝。 我擔心我不能推薦任何東西,因為我不熟悉哪些包可用。

我不知道有問題的實際圖書館,但我想我可以回答你的一些觀點。

我希望DisplayCallback只是一些更復雜的類型簽名的別名。 如果你打開GHCi並導入必要的模塊,你應該可以說

:i DisplayCallback

它會告訴你它的含義。 那么至少你會看到這個定義,即使這並不一定能告訴你它的用途

do記號不只是為IO單子,這對任何單子 如果OpenGL繪圖操作發生在他們自己的專用monad中,我不會感到驚訝。

為什么要指定GLFloat 我想Color3Vector3被定義為包含任何類型的縱坐標。 我們希望它是GLFloat ,因此是類型簽名。 為什么它只在一個坐標上? 我認為Color3的定義要求所有縱坐標都具有相同的類型,因此將其指定為一個會自動導致其他兩個具有相同的類型。

為什么Color3甚至都存在? 為什么我們不能只用三個輸入調用color 嗯,這是一個API設計選擇,但如果Color3Vector3的定義允許您對整個矢量或顏色進行算術運算,我也不會感到驚訝。 因此,通過將坐標放入向量中,您可以將它們視為一個單元,對它們進行算術運算,輕松地將它們存儲在列表中等等。如果您正在完成所有這些操作,那么您實際上並不想要將它們解壓縮所有這些都是為了實際調用OpenGL函數。

美元符號是什么? 好吧,如果我寫

color Color3 r g b

這意味着我正在調用color ,傳遞四個參數: Color3rgb 那不是我們想要的。 我們想要的是什么

color (Color3 r g b)

也就是說,用一個參數調用color 您可以使用括號來執行此操作,也可以使用$代替。 它有點像Unix管道,因為它可以讓你轉向

func3 (func2 (func1 x))

func3 $ func2 $ func1 $ x

如果你有很多功能,那么獲得正確數量的右括號會很煩人。 對於一個函數調用,這是一個品味問題。

renderPrimitive函數renderPrimitive 兩個參數。 其中一個是Quads (無論是什么),另一個是整個do-block 因此,您將所有代碼作為參數傳遞給renderPrimitive函數(可能以某種方式執行它)。

希望這會給你一些啟示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM