2011年10月29日星期六

3. GLUT - OpenGL FAQ

返回 OpenGL FAQ 目錄

3.010 什麼是 GLUT? 它跟 OpenGL 有什麼不一樣?

因為 OpenGL 沒有提供視窗介面系統或使用者輸入之類的的例程功能,應用程式往往必須用只屬於特定平台的例程來達成類似目的,結果是代碼不可移植。

此外,專屬特定平台的例程往往追求強大的全功能,因此只是寫個小程式或小展示也會弄的很複雜。GLUT函式庫解決了這些問題。藉由提供一個平台無關的介面,以簡單優雅的方式來處理視窗管理、選單、還有使用者輸入。使用 GLUT 會有一些靈活性的代價。

3.015 我可以上哪抓 GLUT ?

The GLUT FAQ 和 GLUT source code tree 可供下載。
Nate Robins 的網頁有最新的 GLUT v3.7.5。

3.020 我該用GLUT嗎?

你的應用程式可能想做一些GLUT不允許的事,或者它可能需要使用特定平台的庫來完成非圖形任務。在這種狀況下,可以考慮放棄GLUT作為應用程式的視窗介面與輸入,改用該特定平台的函式庫。

問一問你自己以下問題
  • 我的應用程式只運行在單一平台上嗎?
  • 我需要使用多個的繪製本文嗎 (Rendering Context) ?
  • 我需要在多個繪製本文間共享display list 或紋理物件嗎?
  • 我需要使用的輸入設備,GLUT沒有支援?
  • 我需要特定平台的函式庫來處理一些其他的任務,如文字或者聲音嗎?
如果上面有任何一個答案是Yes,那你需要評估一下GLUT是否是對的選擇。

3.025 GLUT的原始碼授權非常嚴格。有沒有其他替代品呢?

有的,請看看freeglut,它宣稱100%相容取代GLUT 3.7。

3.027 為什麼 glutTimerFunc() 只執行我的回呼函數(callback) 一次?

GLUT 裡用 glutTimerFunc()函數來啟動一個計時器,並指定一個定時回呼函數。它會在指定的一小段時間後執行該回呼函數,然後刪除計時器。這也是許多API計時器的運作方式。

通常來說,不斷重複地執行回呼函數也很實用。在你的回呼函數裡重設計時器就可以實現這一點。例如以下代碼,每次定時回呼函數裡,都例行性的重設了計時器,以便不斷執行定時回呼函數。
static void timerCallback (int value)
{
    /* 在這裡做計時器工作 */
    /* 如果需要的話,呼叫 glutPostRedisplay() */

    /* 經過elapsedUSecs秒數後再呼叫一次 */
    glutTimerFunc (elapsedUSecs, timerCallback, value);
}

3.030 我要指定不同的任務給滑鼠左鍵拖曳和右鍵拖曳。可是我只能指定一個glutMotionFunc() 回呼函數,又沒有左/右鍵參數。

你可以由 glutGetModifiers() 得知 SHIFT,ALT 及 CTRL鍵的狀態,並依據狀態輕鬆地設定不同任務。

想給滑鼠左鍵拖曳與右鍵拖曳指定不同任務,你必須根據按下的滑鼠按鍵,切換拖曳回呼函數(motion function)。你可以用 glutMouseFunc() 所設定的滑鼠回呼函數來做到這一點。滑鼠回呼函數的第一個參數指出是哪個按鍵觸發滑鼠事件 (GLUT_LEFT, GLUT_MIDDLE, or GLUT_RIGHT)。第二個參數指出按鍵狀態(GLUT_UP or GLUT_DOWN)。

為了說明,這兒示範了 glutMouseFunc() 回呼函數的寫法:
/* 分別宣告左右鍵拖曳函數 */
static void leftMotion (int x, int y);
static void rightMotion (int x, int y);
static void mouseCallback (int mouse, int state, int x, int y)
{
    if (state==GLUT_DOWN) {
        /* 當按鍵按下後,設置正確的滑鼠拖曳函數 */
        if (button==GLUT_LEFT)
            glutMotionFunc (leftMotion);
        else if (button==GLUT_RIGHT)
            glutMotionFunc (rightButton);
        }
}

3.040 GLUT 怎麼做到XXX的?

通常想知道GLUT怎麼建立視窗、處裡輸入設備、展示選單、或者等等其他任何其他工作,最佳辦法就是去下載 GLUT 的源代碼然後看看它怎麼寫。

3.050 我該怎麼用GLUT 演示動畫?

GLUT讓應用程式指定一個繪圖回呼函數來畫圖。你可以從其他處呼叫 glutPostRedisplay() 來強制執行該繪圖回呼函數,畫完再把控制權還給 glutMainLoop()。

如果要創建一個每個 frame 之間畫得越快越好的動畫,要使用 glutIdleFunc() 來設定閒置回呼函數。當glutMainLoop() 沒其他事要忙時,它會呼叫這個閒置回呼函數,在這裡頭呼叫glutPostRedisplay()即可。

如果要創建一個每個 frame之間時間間隔固定的定時動畫,把 glutIdelFunc() 替換成 glutTimerFunc()。glutTimerFunc() 將會在指定時間之後呼叫回呼函數。為了持續的更新畫面,你必須在計時器回呼函數裡呼叫 glutPostRedisplay() 並且用 glutTimerFunc() 重置下一次計時器。

3.060 有可能在視窗開啟之後改變視窗大小嗎? (例如我已經呼叫了glutInitWindowSize(); 與 glutCreateWindow();之後)

一旦你的程式已經進入glutMainLoop(),而且已經呼叫了任一回呼函數,你都可以呼叫 glutReshapeWindow(int width, int height) 來改變大小。

請注意,視窗尺寸並不會在呼叫 glutReshapeWindow() 後立刻改變,它僅僅送出一個改變尺寸的訊息給GLUT。當程式控制權回到 glutMainLoop() 後訊息才會被處裡。

3.070 我的GLUT程式一開始配置了一些記憶體空間,我該如何在程式結束時釋放記憶體空間?

如果使用者經由你可以捕捉的輸入來關閉程式,像是一個按鍵或者選單,那答案很簡單。在適當的輸入事件處理裡頭釋放資源即可。

通常來說,這問題只有使用者按下視窗框控制按鈕去關閉程式時才是個問題,就是那顆位於視窗右上角的小叉叉按鈕。這個狀況下,你的程式不會收到任何有關程式要結束的GLUT事件。事實上 glutMainLoop() 也只是很單純的呼叫 exit(0) 來銷毀視窗。

對於一些單純的資源像是記憶體釋放,不用擔心,作業系統會回收任何此process使用的記憶體空間。

比較需要關注的事情,是像彈出視窗提醒使用者儲存檔案,或者把軟體緩衝區保存的數據寫入檔案。

如果你用C++,那最簡便的解法就是把你的GLUT應用程式包成一個C++ class。C++語言保證在物件銷毀時自動呼叫解構函數。

另一個選項是用 ANSI C/C++ 的 atexit() 指定一個終止回呼函數,當程式終止時會執行該函數。你需要把你的緩衝區跟資料都宣告成全域變數,這樣atexit() 回呼函數才有辦法存取那些資料。請參考ANSI C/C++ 手冊取得更多資訊。只有C/C++才可以用atexit()。


3.080 我該如何得知使用者關閉了視窗?

同 3.070 答案。

3.090 怎樣才能讓glutMainLoop() 返回我的呼叫端程式?

(譯註: 比方說你在main() 裡頭呼叫了glutMainLoop() ,那main()就是呼叫端 )
glutMainLoop() 並沒有被設計成會返回呼叫端。GLUT被設計成事件驅動的應用程式,藉由捕捉某些輸入事件來終止程式,像是GLUT選單或者鍵盤事件回呼函數。

如果你堅持一定要從glutMainLoop() 返回,那唯一的一條路就是去下載GLUT原始碼,改寫glutMainLoop()成任何你想要的樣子。然後編譯連結這個修改版本的glutMainLoop()。

3.100 我的GLUT應用程式怎樣才可以擺脫那個黑黑的命令列視窗? 

Visual C++ 6.0下,選擇 Project 選單,Settings… dialog. 選擇 Link 分頁. 在專案選項的編輯框裡,附加 /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup 這行字到最後。(譯註: 我沒試過XD)

3.110 這裡沒有回答我的GLUT問題,我該上哪去找其他資訊? 

關於GLUT資訊,The GLUT FAQ 很棒。


0 意見:

張貼意見