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沒有支援?
- 我需要特定平台的函式庫來處理一些其他的任務,如文字或者聲音嗎?
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()。
0 意見:
張貼意見