Lighthouse3d.com >> GLUT Tutorial >> Avoiding the Idle Func >> glutPostRedisplay
直到所有源代碼都使用顯示函數作為空閑函數.這意味着當沒有任何事件要處理的時候GLUT會調用顯示函數,也就是說,它會盡可能頻繁的調用顯示函數.
這個一個建立交互程序的簡單方法.當你的回調函數處理完鍵盤事件后,顯示函數會自動被調用,屏幕會被重繪.
我們要做的只是把顯示函數和空閑函數注冊到同一個回調函數.
如果我們的程序打算以獨占的方式運行,或者想要獲取測試分數,這是可行的.然而,當我們的程序只是操作系統進程之一時,計算機的資源就會變得不充足.
問題就在於我們的GLUT程序,它過於頻繁的調用顯示函數,即使已經沒有新對象需要渲染.你可以去看下任務管理器中進程的選項卡,你會發現即使幀與幀之間沒有渲染更改,我們的GLUT程序仍然會快速消耗CPU資源.GPU資源毫無疑問的也會被消耗.
當我們需要CPU或GPU資源來做其它事的時候我們就會想節省這些資源,保持我們的GLUT程序的占用行為不生效.
要達到這個目的,我們必須選擇性的告訴GLUT去調用顯示函數.glutPostRedisplay函數就是為了這個用途而設計的.
glutPostRedisplay函數會標記當前窗體來重新顯示,它會促使主循環盡快的調用完顯示函數.注意它只影響當前窗體(獲得焦點的窗體),不是所有窗體.上一個例子有子窗體,我們必須做一些擴展測量來確保工作正常.
首先我們將會為主窗體更改顯示函數,改成它可以調用所有子窗體定義的渲染函數.然后我們只需要在主窗體中調用glutPostRedisplay函數,所有的窗體都會被重新渲染.
在主函數中添加:
int main(int argc, char **argv) { // init GLUT and create main window glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(800,800); mainWindow = glutCreateWindow("Lighthouse3D - GLUT Tutorial"); // callbacks for main window glutDisplayFunc(renderScene); glutReshapeFunc(changeSize); glutIdleFunc(renderSceneAll); ...
我們打算更改主窗體的顯示函數為renderSceneAll,就是上一個空閑函數,我們會先取消空閑函數的原有綁定回調.於是新的main函數如下:
int main(int argc, char **argv) { // init GLUT and create main window glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(800,800); mainWindow = glutCreateWindow("Lighthouse3D - GLUT Tutorial"); // callbacks for main window glutDisplayFunc(renderSceneAll); glutReshapeFunc(changeSize); // Removing the idle function to save CPU and GPU //glutIdleFunc(renderSceneAll); ...
現在來看我們要把glutPostRedisplay函數的調用放在哪里.我們只想要在渲染圖像有更改的時候調用顯示函數.當所有場景處於靜止狀態,渲染差異化的唯一時刻就是當我們移動鏡頭的時候.
鏡頭移動一般是用鼠標鍵盤,所以我們必須添加glutPostRedisplay的調用到這些事件的回調函數中.
現在就先來改鼠標回調函數.鼠標移動時鏡頭跟着移動.所以我們會將來的下面這段代碼:
void mouseMove(int x, int y) { // this will only be true when the left button is down if (xOrigin >= 0) { // update deltaAngle deltaAngle = (x - xOrigin) * 0.001f; // update camera's direction lx = sin(angle + deltaAngle); lz = -cos(angle + deltaAngle); } }
改成下面這樣:
void mouseMove(int x, int y) { // this will only be true when the left button is down if (xOrigin >= 0) { // update deltaAngle deltaAngle = (x - xOrigin) * 0.001f; // update camera's direction lx = sin(angle + deltaAngle); lz = -cos(angle + deltaAngle); // setting the main window as active //and marking it for redraw glutSetWindow(mainWindow); glutPostRedisplay(); } }
現在輪到鍵盤回調函數.處理鍵盤事件的函數叫pressKey.我們會將原來的下面這段代碼:
void pressKey(int key, int xx, int yy) { switch (key) { case GLUT_KEY_UP : deltaMove = 0.5f; break; case GLUT_KEY_DOWN : deltaMove = -0.5f; break; } }
改成下面這樣:
void pressKey(int key, int xx, int yy) { switch (key) { case GLUT_KEY_UP : deltaMove = 0.5f; break; case GLUT_KEY_DOWN : deltaMove = -0.5f; break; } // setting the main window as active //and marking it for redraw glutSetWindow(mainWindow); glutPostRedisplay(); }
不幸的是,我們關閉了鍵盤重復輸入的鍵盤回調函數,因此以上代碼的修改是不夠的.pressKey函數只會被調用一次,不是你預期中的按壓多久就調用多少次.
幸運的是有一個方法可以解決這個問題.當我們按下鍵時設置一個非零值的狀態變量.稍后我們檢查該變量來確定鏡頭位置是否需要更新.該檢查是放在renderSceneAll函數中,所以我們的測試將會放在用戶初始按下鍵的任何地方.
下面是之前例子中renderSceneAll的代碼:
// Global render func void renderSceneAll() { // check for keyboard movement if (deltaMove) { computePos(deltaMove); } renderScene(); renderScenesw1(); renderScenesw2(); renderScenesw3(); }
變量deltaMode在一個鍵最初被按下的時候會被置非零值.因此我們可以在if條件語句中調用glutPostRedisplay函數,實現如下:
// Global render func void renderSceneAll() { // check for keyboard movement if (deltaMove) { computePos(deltaMove); // set the main window as active and // ask for a redraw glutSetWindow(mainWindow); glutPostRedisplay(); } renderScene(); renderScenesw1(); renderScenesw2(); renderScenesw3(); }
做完以上更改后,我們的顯示函數會被重復調用直至deltaMove變量歸零.只有當用戶松開按鍵的時候發生,releaseKey函數實現如下:
void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_UP : case GLUT_KEY_DOWN : deltaMove = 0;break; } }
下一節會給出完整代碼.