1. 程式人生 > >用PyOpenGL叩開3D的心扉——OpenGL全解析(3)

用PyOpenGL叩開3D的心扉——OpenGL全解析(3)

第一個PyOpenGL程式

說實話我們OpenGL的基礎還遠遠沒有學完,不過我在說下去大概就不會有人看了,所以,雖然稍稍有些早,開始我們的第一個程式吧。

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

def drawFunc():
    glClear(GL_COLOR_BUFFER_BIT)
    #glRotatef(1, 0, 1, 0)
    glutWireTeapot(0.5)
    glFlush()

glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow("First")
glutDisplayFunc(drawFunc)
#glutIdleFunc(drawFunc)
glutMainLoop()

注意,這裡執行可能會出現這樣的錯誤。
_base_glutInit( ctypes.byref(count), holder )
TypeError: 'NoneType' object is not callable
如果有,在網上下載glut32.dll檔案,放到c:\windows\SysWOW64資料夾中就可以正常運行了。

結果如上圖,雖然很短,相信這裡面很多函式大家會覺得陌生,不要緊,這些程式碼是每個程式中都需要的,基本看兩個就OK了,下面來詳細說明一下。

先把註釋的兩個函式給無視了,看看一開始三行的匯入,OpenGL一開始是以C寫成的,所以OpenGL的程式中繪圖的程式碼也是一大堆的函式,沒有什麼物件類的,所以我們把相關函式全部倒入,雖然這有些汙染環境的感覺和Python的設計機理有些不符,不過這麼多年都過來了:),而且OpenGL中的函式都是有字首的,問題也不大。


OpenGL函式的命名規則

一般的函式命名如下:

<字首><根函式><引數數目><引數型別>

字首有gl、glu、aux、glut、wgl、glx、agl等等,分別表示該函式屬於OpenGL那個開發庫等。所謂開發庫,要知道原生的OpenGL是跨平臺的,跨平臺意味著很多功能是無法實現的,比如說Windows和X-Window的視窗實現機制是不同的,OpenGL並不關心這些東西,只管畫圖。所以,OpenGL並沒有視窗函式,比如無法建立視窗,無法獲得輸入……這些東西都需要其他的函式庫來實現。我們可以用我之前講述的Pygame來建立視窗,然後用PyOpenGL來繪圖,不過咱不能要求每個看PyOpenGL教程的朋友都先看一遍Pygame教程(這樣就是強買強賣了不是),所以這裡就用OpenGL常用的工具函式庫來實現。

我們主要使用兩種,一個是GLU庫,它提供了比較基礎的命令的封裝,可以很簡單的實現比較多的複雜功能;而另外一個就是GLUT,glut是不依賴於視窗平臺的OpenGL工具包,目的是隱藏不同視窗平臺API的複雜度,提供更為複雜的繪製功能,我們會大量的使用它。

引數數和引數函式就很好理解了,有點像匈牙利命名法,f說明是個float,i說明是int等等。對Python來說可能不是很重要,不過還是要說明一下,OpenGL函式有d(double)的版本,C/C++語言一般預設浮點數就是double,使用d版本函式可能會顯得比較簡單,但是我們不推薦。因為OpenGL內部資料都是以float的形式存放的,如果使用double會對效能有一定的影響。

舉個例子,glColor3f()表示了該函式屬於gl庫,引數是三個float型引數指標。類似的函式還有glColor3i,glColor4f等,我們用glColor*()來表示這一類函式。

初始化視窗

11~17行基本也是固定的,

glutInit()是用glut來初始化OpenGL的,所有的問題都交給這個函式吧,基本不用管,雖說可以接受引數的,基本無用。

glutInitDisplayMode(MODE)非常重要,這裡告訴系統我們需要一個怎樣顯示模式。至於其引數GLUT_RGBA就是使用(red, green, blue)的顏色系統。有沒有寫錯?這裡有個A啊,不應該是(red, green, blue, alpha)麼?大概是歷史原因,GLUT_RGBA和GLUT_RGB是其實是等價的(坑爹啊),要想實現Alpha還得用其他的引數。而GLUT_SINGLE意味著所有的繪圖操作都直接在顯示的視窗執行,相對的,我們還有一個雙緩衝的視窗,對於動畫來說非常合適。看看用Python和Pygame寫遊戲-從入門到精通(3)有些說明。

glutInitWindowSize(400, 400)這個函式很容易理解,設定出現的視窗的大小。實際上還有個glutInitWindowPosition()也很常用,用來設定窗口出現的位置。

glutCreateWindow(“First”),一旦呼叫了,就出現一個視窗了,引數就是視窗的標題。

glutDisplayFunc(func)是glut非常討人喜歡的一個功能,它註冊了一個函式,用來繪製OpenGL視窗,這個函式裡就寫著很多OpenGL的繪圖操作等命令,也就是我們主要要學習的東西。

glutMainLoop(),主迴圈,一旦呼叫了,我們的OpenGL就一直執行下去了。和很多程式中的主迴圈一樣,不停的執行,畫出即時的影象,處理輸入等。


繪圖

看看drawFunc裡的幾句話,這裡是實際繪圖的函式。

glClear(GL_COLOR_BUFFER_BIT)是把先前的畫面給清除,這基本是定律,每次重繪之前都要把原來的畫面擦除,否則疊加起來什麼都看不出了。glClear一看就知道是OpenGL原生的命令,而引數就是指明要清除的buffer。大家一定會有疑問,我們清除,不就是清除螢幕上的畫面麼,為什麼還要指定?OpenGL的博大精深這裡就體現出來了,buffer不僅僅有我們看到的那個GL_COLOR_BUFFER_BIT,OpenGL中還有其他的buffer型別,我們會在後面的章節講到。

glutWireTeapot(0.5)是glut提供的繪製猶他茶壺的工具函式,茶壺還是相當複雜的一個幾何體,用這個函式一下子就畫出來了,不過基本也就演示用用。這裡是用的線模型,因為沒有說光照和材質,如果glutSolidTeapot()畫出來就成紙片兒了。

glFlush()似乎不用多說,畫了那麼多,自然要重新整理一下顯示。不過,這裡的重新整理不僅僅是螢幕上的更新,實際上,它是處理OpenGL的渲染流水線,讓所有排隊中的命令得到執行。OpenGL的渲染流水線是一個很重要的概念,不過這裡暫時還不打算多說明,否則對初學者來說,未免有些麻煩了。但是這並不意味著可以無視這些基礎,知道怎麼做只能讓你優秀,知道為什麼這麼做才能讓你卓越


小驚喜

現在你可以把註釋的兩個語句打開了,執行以下看到什麼?旋轉的茶壺!不得不說帥多了~

glutIdleFunc(Func)又是一個激動人心的函式,可以讓OpenGL在閒暇之餘,呼叫一下注冊的函式,這是是產生動畫的絕好方法。

glRotatef(1, 0, 1, 0)是一個我們以後會詳細講的函式,簡單來說四個引數第一個是角度,後三個是一個向量,意義就是繞著這個向量旋轉,這裡是繞著Y軸旋轉1°。這一度一度的累加,最後使得茶壺圍繞Y軸不停的旋轉。從這裡我們也能看出來,我們指定了一個旋轉的角度後,重新繪製並不會復位,而是在上一次旋轉的結果上繼續旋轉。這是一個非常重要的概念,OpenGL是一個狀態機,一旦你指定了某種狀態,知道再指定位置,它會保持那種狀態。不僅僅是旋轉,包括以後的光照貼圖等等,都遵循這樣的規律。

好了,我們有了第一個PyOpenGL程式了,雖然離我們詳細中的五光十色的立體世界還有些差距,不過畢竟畫了點東西出來了(要知道,猶他茶壺在3D技術發展之初,是里程碑一般的作品)。慢慢的,我們會充實自己的知識,繪製出更靚麗的畫面。