OpenGL第六讲——动画
Chapter6 動畫
6.1 雙緩沖技術
實際的動畫是事先都畫好,然后再拿出來顯示;但是計算機的動畫是畫一張就拿出來一張,再畫下一張,如果畫的圖形比較復雜,則可能只畫了一半就被觀眾看到了,這樣會導致屏幕的閃爍。
于是可以假設有2張畫板,畫圖的人畫好了就與掛在屏幕上的畫板交換,這在計算機圖形學中被稱為雙緩沖技術。
雙緩沖技術:在存儲器(很有可能是顯存)中開辟兩塊區域,一塊作為發送到顯示器的數據,一塊作為繪畫的區域,在適當的時候交換它們。由于交換兩塊內存區域實際上只需要交換兩個指針,這一方法效率非常高,所以被廣泛的采用。
用GLUT工具包實現雙緩沖:
之前我們寫過glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE),其中的GLUT_SINGLE表示單緩沖,如果要使用雙緩沖改成GLUT_DOUBLE就行。
同時,還需要交換兩個緩沖區,只需要在繪制完成時簡單的調用glutSwapBuffers函數就可以了。
glutIdleFunc可以做到在CPU空閑時調用某一函數。
太陽、地球、月亮程序的動畫:
#include <GL/glut.h> // 太陽、地球和月亮 // 假設每個月都是30天 // 一年12個月,共是360天 static int day = 200; // day的變化:從0到359 void myDisplay(void) {//glEnable(GL_DEPTH_TEST); //啟動深度測試(這樣后繪制的圖形如果在已經存在的圖形的前面,它會被遮住,而不是遮住別人glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清空顏色和深度緩沖glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(75, 1, 10, 400000000);glMatrixMode(GL_MODELVIEW);glLoadIdentity();gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);// 繪制紅色的“太陽”glColor3f(1.0f, 0.0f, 0.0f);glutSolidSphere(69600000, 20, 20);// 繪制藍色的“地球”glColor3f(0.0f, 0.0f, 1.0f);glRotatef(day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(150000000, 0.0f, 0.0f);glutSolidSphere(15945000, 20, 20);// 繪制黃色的“月亮”glColor3f(1.0f, 1.0f, 0.0f);glRotatef(day / 30.0 * 360.0 - day / 360.0 * 360.0, 0.0f, 0.0f, -1.0f);glTranslatef(38000000, 0.0f, 0.0f);glutSolidSphere(4345000, 20, 20);glFlush();glutSwapBuffers(); }void myIdle(void) {/* 新的函數,在空閑時調用,作用是把日期往后移動一天并重新繪制,達到動畫效果 */++day;if (day >= 360)day = 0;myDisplay(); }int main(int argc, char* argv[]) {glutInit(&argc, argv);//對GLUT進行初始化,這個函數必須在其它的GLUT使用之前調用一次glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); //設置顯示方式glutInitWindowPosition(100, 100); //設置窗口位置glutInitWindowSize(400, 400);//窗口大小glutCreateWindow("動畫"); //根據前面設置的信息創建窗口。參數將被作為窗口的標題。glutDisplayFunc(&myDisplay); //當需要畫圖時,請調用myDisplay函數glutIdleFunc(&myIdle);glutMainLoop(); //進行一個消息循環return 0; }只是在myDisplay的最后加上了交換緩沖區的操作glutSwapBuffers(),然后增加了一個CPU空閑時就執行的函數myIdle,其中調用了myDisplay重新畫。
6.2 垂直同步
代碼是寫好了,但相信大家還有疑問。某些朋友可能在運行時發現,雖然CPU幾乎都用上了,但運動速度很快,根本看不清楚,另一些朋友在運行時發現CPU使用率很低,根本就沒有把空閑時間完全利用起來。但對于上面那段代碼來說,這些現象都是合理的。這里就牽涉到關于垂直同步的問題。
大家知道顯示器的刷新率是比較有限的,一般為60-120Hz,也就是一秒鐘刷新60-120次。但如果叫計算機繪制一個簡單的畫面,例如只有一個三角形,則一秒鐘可以繪制成千上萬次。因此,如果最大限度的利用計算機的處理能力,繪制很多幅畫面,但顯示器的刷新速度卻跟不上,這不僅造成性能的浪費,還可能帶來一些負面影響(例如,顯示器只刷新到一半時,需要繪制的內容卻變化了,由于顯示器是逐行刷新的,于是顯示器上半部分和下半部分實際上是來自兩幅畫面)。采用垂直同步技術可以解決這一問題。即,只有在顯示器刷新時,才把繪制好的圖象傳輸出去供顯示。這樣一來,計算機就不必去繪制大量的根本就用不到的圖象了。如果顯示器的刷新率為85Hz,則計算機一秒鐘只需要繪制85幅圖象就足夠,如果場景足夠簡單,就會造成比較多的CPU空閑。
幾乎所有的顯卡都支持“垂直同步”這一功能。
垂直同步也有它的問題。如果刷新頻率為60Hz,則在繪制比較簡單的場景時,繪制一幅圖畫需要的時間很段,幀速可以恒定在60FPS(即60幀/秒)。如果場景變得復雜,繪制一幅圖畫的時間超過了1/60秒,則幀速將急劇下降。
如果繪制一幅圖畫的時間為1/50,則在第一個1/60秒時,顯示器需要刷新了,但由于新的圖畫沒有畫好,所以只能顯示原來的圖畫,等到下一個1/60秒時才顯示新的圖畫。于是顯示一幅圖畫實際上用了1/30秒,幀速為30FPS。(如果不采用垂直同步,則幀速應該是50FPS)
如果繪制一幅圖畫的時間更長,則下降的趨勢就是階梯狀的:60FPS,30FPS,20FPS,……(60/1,60/2,60/3,……)
如果每一幅圖畫的復雜程度是不一致的,且繪制它們需要的時間都在1/60上下。則在1/60時間內畫完時,幀速為60FPS,在1/60時間未完成時,幀速為30FPS,這就造成了幀速的跳動。這是很麻煩的事情,需要避免它——要么想辦法簡化每一畫面的繪制時間,要么都延遲一小段時間,以作到統一。
回過頭來看前面的問題。如果使用了大量的CPU而且速度很快無法看清,則打開垂直同步可以解決該問題。當然如果你認為垂直同步有這樣那樣的缺點,也可以關閉它?!劣谌绾未蜷_和關閉,因操作系統而異了。具體步驟請自己搜索之。
當然,也有其它辦法可以控制動畫的幀速,或者盡量讓動畫的速度盡量和幀速無關。不過這里面很多內容都是與操作系統比較緊密的,況且它們跟OpenGL關系也不太大。這里就不做介紹了。
6.3 計算幀速
幀速就是一秒鐘內播放的畫面數目(FPS)
總結
以上是生活随笔為你收集整理的OpenGL第六讲——动画的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python调用百度地图api路径查询
- 下一篇: BigBrother的大数据之旅Day