資料來源:http://eyehere.net/2011/learn-opengl-3d-by-pyopengl-4/
用PyOpenGL叩開3D的心扉——OpenGL全解析(4)
星期日, 6. 十一月2011
圖元
上一次,我們有了一個足夠3D的程序了,雖然很漂亮,但是那個茶壺並不是我們畫出來的,glut帶給我們的便利而已。從現在開始我們就得自己動手豐衣足食了,為了達到這一點,我們得再了解一些OpenGL的一些知識。
如右圖所示,你必須知道構成我們3D圖像的最小單位,它們往往被稱為圖元。
- 點,在OpenGL中,這是最基本的圖元,比如說圖中紅色的那個點。
- 線,比如左圖中粉色的那根。我們可以看到,兩個點定一條線,不過從一個點上可以發射出任意多的線,所以點和線的數量關係並不是確定的。
- 多邊形是最為複雜的圖元,比如左圖的黃色梯形。和數學中的多邊形含義是一樣的。
在標準OpenGL中,既然稱為多邊形,自然不一定是四個邊,可以是任意,標準OpenGL中還有一個專門的矩形繪圖函數glRect*,不過在OpenGL ES中,多邊形就是指三角形,出於各種考慮,不支持更複雜的多邊形。
觀察左邊的圖像,這是一個球形,不過每一個小多邊形都是平面,這麼一個個的小平面,最終組成了球面。這是一個非常重要的概念,在計算機圖像中,曲線和曲面是非常難表示的,真正的數學概念上的“圓”,相鄰的三個點不在一條直線上,球也類似。但是如果在計算機上這麼處理的話,代價太大了,所以我們總是把一個光滑的線和麵分解成多個斷線或小平面,就好像一個看台,遠看是圓形的,但是實際上是由一塊塊的磚拼成的。當然這是比較一般的做法,OpenGL也有真正意義上的曲線曲面的表示方法,比如如雷貫耳的“貝塞爾曲線/面”,這個是比較高級的話題,再議。
如上圖,一個球形由不同數量的平面組成時的狀況,下面的數字標示圍繞一周的多邊形的數量,也就是上面和下面頂點發散出三角形的數量,如果給個名詞的話,我們可以叫它“段”。我們可以看到,當段數為32的時候,很漂亮的球形;16的時候,還行,能分辨出這是球形;8的時候就有些丟人了;而沒人會把段數為4的那個物體叫球體,叫水晶體才能接受。
繪製圖元
因為點是最為基礎的圖元,我們得首先了解它:
glVertex(x, y[, z[, w]])
我們最多可以使用4個參數,一般2D的用兩個,形式為glVertex2f(x, y);3D的用三個,自然就是glVertex3f(x, y, z);w在我們用到的時候再說。
那麼畫點是不是就調用這個函數就可以了呢?雖然差不多但可惜實際不完全是,這裡有一個初學者感到很困惑的東西再裡頭,OpenGL所有的繪圖指令,都必須包含在glBegin()和glEnd()之間!為什麼要這樣?glBegin()的參數告訴OpenGL這些點最終的繪製方法,如果單純是畫點GL_POINTS,這畫三個點就是孤立的畫出來,如果是其他的比如多邊形GL_POLYGON,那麼畫三個點結果就是組成一個三角形。雖然繪製點的方法完全一樣,因為給的參數不同而導致了不同的結果。glBegin()提供的圖元繪圖方式如下:
參數 | 含義 |
---|---|
GL_POINTS | 單個頂點集 |
GL_LINES | 線段 |
GL_LINE_STRIP | 不閉合的連續線段 |
GL_LINE_LOOP | 閉合的線段 |
GL_POLYGON | 多邊形 |
GL_TRAINGLES | 獨立三角形 |
GL_TRAINGLE_STRIP | 三角形串,線性連續 |
GL_TRAINGLE_FAN | 三角形串,扇狀連續 |
GL_QUADS | 獨立四邊形 |
GL_QUAD_STRIP | 四邊形串 |
很明顯,這樣說,原來理解的人都要迷糊半天,所以“一圖胜千言”來了,請參考下圖理解,
如果還有有些疑惑,可以使用下面的程序,修改加深理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * def init(): glClearColor( 0.0 , 0.0 , 0.0 , 1.0 ) gluOrtho2D( - 1.0 , 1.0 , - 1.0 , 1.0 ) def drawFunc(): glClear(GL_COLOR_BUFFER_BIT) glBegin(GL_LINES) glVertex2f( - 1.0 , 0.0 ) glVertex2f( 1.0 , 0.0 ) glVertex2f( 0.0 , 1.0 ) glVertex2f( 0.0 , - 1.0 ) glEnd() glPointSize( 5.0 ) glBegin(GL_POINTS) glColor3f( 1.0 , 0.0 , 0.0 ) glVertex2f( 0.3 , 0.3 ) glColor3f( 0.0 , 1.0 , 0.0 ) glVertex2f( 0.6 , 0.6 ) glColor3f( 0.0 , 0.0 , 1.0 ) glVertex2f( 0.9 , 0.9 ) glEnd() glColor3f( 1.0 , 1.0 , 0 ) glBegin(GL_QUADS) glVertex2f( - 0.2 , 0.2 ) glVertex2f( - 0.2 , 0.5 ) glVertex2f( - 0.5 , 0.5 ) glVertex2f( - 0.5 , 0.2 ) glEnd() glColor3f( 0.0 , 1.0 , 1.0 ) glPolygonMode(GL_FRONT, GL_LINE) glPolygonMode(GL_BACK, GL_FILL) glBegin(GL_POLYGON) glVertex2f( - 0.5 , - 0.1 ) glVertex2f( - 0.8 , - 0.3 ) glVertex2f( - 0.8 , - 0.6 ) glVertex2f( - 0.5 , - 0.8 ) glVertex2f( - 0.2 , - 0.6 ) glVertex2f( - 0.2 , - 0.3 ) glEnd() glPolygonMode(GL_FRONT, GL_FILL) glPolygonMode(GL_BACK, GL_LINE) glBegin(GL_POLYGON) glVertex2f( 0.5 , - 0.1 ) glVertex2f( 0.2 , - 0.3 ) glVertex2f( 0.2 , - 0.6 ) glVertex2f( 0.5 , - 0.8 ) glVertex2f( 0.8 , - 0.6 ) glVertex2f( 0.8 , - 0.3 ) glEnd() glFlush() glutInit() glutInitDisplayMode(GLUT_RGBA|GLUT_SINGLE) glutInitWindowSize( 400 , 400 ) glutCreateWindow( "Sencond" ) glutDisplayFunc(drawFunc) init() glutMainLoop() |
運行結果如右圖:
分割線:
由12~17行繪製的,這裡我們沒有指定顏色,所以使用OpenGL默認的顏色系統,即前景白色,背景黑色。
右上區域:
由19~27繪製,
glPointSize(5.0)
指明每個點的大小為5個像素(否則默認是一個像素看不清楚,當然不是必要的)。而glColor3f(R, G, B)
指定了繪製的顏色,這裡的RGB都是0~1之間的浮點數,注意這裡的排布,glColorx是可以放在glBegin()和glEnd()裡面的,而glPointSize()則不是。這裡簡單畫了三個不同顏色的點。
左上區域:
我們使用GL_QUADS畫了一個黃色的矩形,很簡單,看起來沒有什麼特別要說明的。真的如此麼?我們畫了幾個點,指定矩形,但是注意到了麼,這個矩形是填充的。也就是說,OpenGL在默認情況下,會填充我們畫出來的圖形。如何不填充?
下區域:
下面兩個圖案請合起來看,這兩個圖形是完全一樣的,代碼分別為左(37~47)和右(49~58),坐標就是一個正一個負而已,唯一不同的是這裡
glPolygonMode(GL_FRONT, GL_LINE)
glPolygonMode(GL_BACK, GL_FILL)
和
glPolygonMode(GL_FRONT, GL_FILL)
glPolygonMode(GL_BACK, GL_LINE)
這個是什麼意思呢?
看看上面解釋各種參數的圖,我們可以看到這裡面的箭頭都是逆時針繪製的,這裡面是有原因的——“這個世界上沒有偶然,有的只有必然”,OpenGL中,每一個面都有正面和反面,這很容易理解,就好像硬幣的兩面一樣。默認情況下,頂點逆時針的那一面,在OpenGL中為正面,當然我們可以更改這種設置,不過何必呢?
glPolygonMode()指定瞭如何繪製面的方式,GL_LINE為只畫線,GL_FILL則是默認的填充。觀察一下代碼和結果,是否很不錯呢?
不過這不是我想說的重點,我們把渲染理解成在面上塗油漆,那麼,如果六個面組成了一個盒子,我們給這個盒子上色的時候,一般就是把外面塗一遍就好了,裡面都刷豈不是浪費油漆浪費時間?這樣一個樸素的道理,在OpenGL裡也是適用的,立體的物體都是由面組成的,很多情況下是封閉的,小到球、盒子,大到人體,就是由面組成的閉合物體。
我們可以告訴OpenGL,反面就不要渲染了,這叫“ 剔除(Cull)”使用glCullFace()可以做到這一點,接受的參數為GL_FRONT , GL_BACK , GL_FRONT_AND_BACK,意義的話,一目了然。這裡先建立一個概念,具體不過必須要先啟用glEnable(GL_CULL_FACE),我們會在用到的時候再詳細說明。
PyOpenGL的安裝
這裡才講述PyOpenGL的安裝有些不應該了,我原以為使用這個東西的人應該會很容易裝上,不過既然是“入門教程”,應該想到還是有不少剛剛入門的朋友在,講一下比較好。
首先PyOpenGL的官方網站,是依附於sourceforge的(好窮啊),網址是http://pyopengl.sourceforge.net/,上面有最新的下載的地址,舉個例子http://pypi.python.org /pypi/PyOpenGL/3.0.1,這裡面有三個文件PyOpenGL-3.0.1.tar.gz和PyOpenGL-3.0.1.zip是一回事,都是源碼,如果你不是Windows,那麼就需要下載這個編譯安裝,如果是的話,就下載PyOpenGL-3.0.1.win32.exe安裝,注意這個是32位的。
雖然我的Windows是64位的,Python也有64位的版本,但是Python很多庫都是32位的,所以我安裝的Python也是32位的,減少了很多麻煩,否則很多時候就是裝上能用,也會出現莫名其妙的問題。
至於Linux等,easy_install是個安裝Python庫很不錯的選擇,當然直接下載源代碼python setup.py也很簡單有效,這些通用的方法,我就不多講了,不明白的請自己google一下。
沒有留言:
張貼留言