Win32 OpenGL 編程 ( 3 ) 基本圖元(點(diǎn),直線,多邊形)的繪制
write by 九天雁翎 (JTianLing) -- blog.csdn.net/vagrxie
一、 提要
在前面兩篇相關(guān)文章
《 Win32 OpenGL 編程(1)Win32下的OpenGL編程必須步驟 》
《 Win32 OpenGL編程(2) 尋找缺失的OpenGL函數(shù) 》
中,我們已經(jīng)建立了一個(gè)較為全面的 Win32 OpenGL 編程環(huán)境及一個(gè)簡(jiǎn)單的框架,并且,實(shí)際上掌握了 OpenGL 在 Windows 下與 Win32 窗口交互的方法,在此基礎(chǔ)上,總算是可以正式進(jìn)行一些 OpenGL 相關(guān)知識(shí)的學(xué)習(xí),前面的那些也就算是熱身。本文的目的是將 OpenGL 中基本圖元(點(diǎn),線,多邊形)的繪制大概的講解一遍,最后可以組合的使用這些技術(shù)用 OpenGL 完成較為復(fù)雜的 2D 圖形。
二、 基本圖元相關(guān)概念
首先講講相關(guān)的概念,在 OpenGL 中,即使是復(fù)雜的圖形,實(shí)際上也是由一些非常基本的圖元組成,即點(diǎn),直線,多邊形,多邊形中用的較多的又是三角形和矩形。在數(shù)學(xué)中,兩點(diǎn)確定一條直線,三點(diǎn)確定一個(gè)三角形和一個(gè)面,同一個(gè)面上的四個(gè)點(diǎn)確定一個(gè)四邊形。。。。。。在 OpenGL 中也大致的利用此方式來(lái)確定直線和多邊形,也就是說(shuō),當(dāng)你想畫一個(gè)直線或者一個(gè)多邊形的時(shí)候,只需要告訴 OpenGL 能確定此直線或者多邊形的點(diǎn)即可。用參考 2 中的描述是:“在 OpenGL 中,所有的幾何物體最終都描述成一組有序的頂點(diǎn)”。有此基本的概念后就可以看下面的例子了。
三、 OpenGL 的 Hello World 示例分析
這里的 Hello World 程序指的是一個(gè)利用 OpenGL 完成的矩形繪制程序,相對(duì)于在系列文章 1 中的 Win32 OpenGL 編程框架,簡(jiǎn)化了很多東西,只剩下最最基本的 OpenGL 元素,但也是一個(gè)完善的 OpenGL 示例了。此示例顯示的是一個(gè)白色的矩形,運(yùn)行效果如附圖 1 ,完整代碼見我博客代碼的 2009-10-12/SimpleRectangle 工程,具體下載及查看方法見本文最后的說(shuō)明。
此示例 OpenGL 相關(guān)的主要就是兩部分。
//OpenGL 初始化開始
void SceneInit ( int w , int h )
{
}
// 這里進(jìn)行所有的繪圖工作
void SceneShow ( GLvoid )
{
glClear ( GL_COLOR_BUFFER_BIT );
glBegin ( GL_QUADS );
glVertex3f (-0.5, -0.5, 0.0);
glVertex3f (0.5, -0.5, 0.0);
glVertex3f (0.5, 0.5, 0.0);
glVertex3f (-0.5, 0.5, 0.0);
glEnd ();
glFlush ();
}
我們下面從 OpenGL 的角度來(lái)分析此程序。作為 HelloWorld 級(jí)的程序,我簡(jiǎn)化了很多東西,首先 OpenGL 的初始化省略了,用的都是 OpenGL 的默認(rèn)值,具體有哪些,后面一步一步說(shuō)。以下是按步驟說(shuō)明每個(gè) OpenGL 函數(shù)。
1. glClear
OpenGL 參考手冊(cè) :
glClear takes a single argument that is the bitwise OR of several values indicating which buffer is to be cleared.
GL_COLOR_BUFFER_BIT Indicates the buffers currently enabled for color writing.
作用是清除顏色緩沖區(qū)。類似于我們使用一塊新申請(qǐng)的內(nèi)存時(shí)先用 memset/ZeroMemory 去清零一下,用這樣的 Clear 操作為我們需要使用的顏色緩沖區(qū) (Color Buffer) 清零一下。假如沒有這樣的操作,以前留在顯存 / 內(nèi)存 ( 不確定 ) 中的值會(huì)影響我們的操作,并且,這樣的問(wèn)題往往是非常難以調(diào)試和發(fā)現(xiàn)的,這一點(diǎn)大家可以嘗試一下。
2. glBegin , glEnd
OpenGL 參考手冊(cè) :
glBegin, glEnd - delimit the vertices of a primitive or a group of like primitives
OpenGL 代碼的主體部分是:
glBegin ( GL_QUADS );
glVertex3f (-0.5, -0.5, 0.0);
glVertex3f (0.5, -0.5, 0.0);
glVertex3f (0.5, 0.5, 0.0);
glVertex3f (-0.5, 0.5, 0.0);
glEnd ();
glBegin 與 glEnd 很明顯是一對(duì), 標(biāo)志著一組 OpenGL 操作的開始和結(jié)束。 并且在參數(shù)中告訴了 OpenGL 下面的操作是針對(duì)什么圖形進(jìn)行的,此例中 GL_QUADS 是表示四邊形。事實(shí)上還有很多其他的參數(shù)來(lái)表示各類圖形,在《 OpenGL Programming Guide 》的 此頁(yè) 中 Figure 2-7 : Geometric Primitive Types 一圖形象的說(shuō)明了各個(gè)參數(shù)的作用。
3. glVertex *
glVertex3f 就是在 基本圖元相關(guān)概念 一節(jié)提到的 OpenGL 中確定頂點(diǎn)的函數(shù)。簡(jiǎn)而言之,上面的 4 句 glVertex3f 確定了矩形的 4 個(gè)頂點(diǎn)。(注意順序)然后, OpenGL 就會(huì)自動(dòng)根據(jù) glBegin 指定的參數(shù)去完成相關(guān)的繪制任務(wù)了, 此例中 GL_QUADS 是表示四邊形,所以最后的效果是一個(gè)矩形,實(shí)際的其他參數(shù)讀者可以自己嘗試一下。
4. OpenGL 默認(rèn)坐標(biāo)系
我們?cè)倏匆幌? glVertex* 指定頂點(diǎn)的代碼:
glVertex3f (-0.5, -0.5, 0.0);
glVertex3f (0.5, -0.5, 0.0);
glVertex3f (0.5, 0.5, 0.0);
glVertex3f (-0.5, 0.5, 0.0);
為什么上述就指定了一個(gè)矩形的四個(gè)頂點(diǎn)呢?需要說(shuō)明的是,在 OpenGL 中默認(rèn)坐標(biāo)體系與 Windows 中常用的不同, Windows 中常用的坐標(biāo)體系(僅 2D )是用戶區(qū)的左上角為坐標(biāo)原點(diǎn),即 (0.0,0.0) 點(diǎn),右為坐標(biāo)軸的 X 軸正方向,下為 Y 軸正方向, OpenGL 中的坐標(biāo)軸( 3D )默認(rèn)以客戶區(qū)中心點(diǎn)為坐標(biāo)原點(diǎn)( 0.0,0.0,0.0 ),右為坐標(biāo)軸的 X 軸正方向,上為 Y 軸正方向,垂直指出屏幕的方向?yàn)? Z 軸正方向。長(zhǎng)度定義是將客戶區(qū)范圍為按單位長(zhǎng)度定義,即整個(gè)客戶區(qū)恰好是 (-1,-1) (左下)到 (1,1) (右上)。附圖 2 是一個(gè)上述示例程序附上 OpenGL 的平面坐標(biāo)系的圖,也可以作為 OpenGL 默認(rèn)坐標(biāo)系的參考圖。
5. OpenGL 函數(shù)命名
這里順面介紹一下 OpenGL 函數(shù)的命名規(guī)范,因?yàn)? C 語(yǔ)言天生的弱點(diǎn)及豐富的數(shù)據(jù)類型,在 OpenGL 中凡是牽涉到與參數(shù)數(shù)量和數(shù)據(jù)類型相關(guān)的函數(shù),一般的命名方式都是 xxxx[n][t] 。
xxxx 表示函數(shù)的意義, [t] 用于表示此函數(shù)對(duì)應(yīng)的類型。一般用單個(gè)的字母表示參數(shù)的類型, s 表示 16 位整數(shù)( OpenGL 中將這個(gè)類型定義為 GLshort ), i 表示 32 位整數(shù)( OpenGL 中將這個(gè)類型定義為 GLint 和 GLsizei ), f 表示 32 位浮點(diǎn)數(shù)( OpenGL 中將這個(gè)類型定義為 GLfloat ), d 表示 64 位浮點(diǎn)數(shù)( OpenGL 中將這個(gè)類型定義為 GLdouble )。此例中使用的是 32 位浮點(diǎn)數(shù),所以是 f 。這是 C 語(yǔ)言沒有函數(shù)重載機(jī)制的天生弱點(diǎn)導(dǎo)致的扭曲應(yīng)對(duì)方案。(用 C++ 就不需要這么麻煩了)
然后是數(shù)字,因?yàn)橥瑯拥脑颍? C 語(yǔ)言中一個(gè)同樣意義的函數(shù)不能同時(shí)有不同個(gè)數(shù)的參數(shù),所以 OpenGL 用一個(gè)數(shù)字來(lái)表示參數(shù)的個(gè)數(shù),此例中是 3 ,表示以 3 個(gè)參數(shù)(即點(diǎn)的 X,Y,Z 坐標(biāo))來(lái)表示頂點(diǎn)。(事實(shí)上還有 glVertex2* , glVertex4* )
比如此例中,用如下代碼效果是一樣的:
glVertex2f (-0.5, -0.5);
glVertex2f (0.5, -0.5);
glVertex2f (0.5, 0.5) ;
glVertex2f (-0.5, 0.5);
在 glVertex2f 中, Z 軸默認(rèn)為 0.
這里我依照參考 2 的用法,以 * 作為通配符來(lái)表示一組函數(shù), * 既可以表示代表參數(shù)數(shù)量的數(shù)字也可以表示代表類型的字母。
6. glFlush
OpenGL 參考手冊(cè) :
glFlush - force execution of GL commands in finite time
說(shuō)白了就是強(qiáng)制執(zhí)行已經(jīng)指定的 OpenGL 命令,與 fflush 命名類似作用也類似。
7. 小結(jié)
以上的 5 個(gè) OpenGL 函數(shù)就構(gòu)成了一個(gè)基本的 OpenGL 程序(不包括模板中使用的那些),由 glClear 清空顏色緩沖區(qū)獲得干凈的環(huán)境,由 glBegin 指定開始一組頂點(diǎn)操作的開始,并確認(rèn)繪制圖形,由 glVertex* 指定頂點(diǎn),由 glEnd 表示操作結(jié)束,由 glFlush 強(qiáng)制開始繪圖。運(yùn)行效果如附圖 1 。
四、 基本圖元
事實(shí)上,上一節(jié)已經(jīng)包含了所有的知識(shí),各個(gè)圖元在 OpenGL 中繪制方式的不同僅僅在于 glBegin 的參數(shù)不同而已,這里將基本圖元簡(jiǎn)單介紹一下。
1. 點(diǎn)
點(diǎn)是最常用的圖元之一了,而且所有的圖像都可以看做是由點(diǎn)構(gòu)成的,事實(shí)上屏幕也就是由一個(gè)一個(gè)像素點(diǎn)構(gòu)成了圖像:)
畫點(diǎn)的方式是使用 GL_POINTS 為參數(shù)調(diào)用 glBegin 。那么,每一個(gè) glVertex* 指定頂點(diǎn)就會(huì)繪制成一個(gè)單獨(dú)的點(diǎn)。點(diǎn)的默認(rèn)大小為 1 個(gè)像素,可以通過(guò) glPointSize 函數(shù)來(lái)改變點(diǎn)的大小,在點(diǎn)比較大并且沒有開啟抗鋸齒時(shí),是按照一個(gè)正方形來(lái)繪制的。比如上例中,僅僅將 GL_QUADS 換成 GL_POINTS ,將是繪制上述矩形的 4 個(gè)頂點(diǎn),為了截圖效果顯著,調(diào)用 glPointSize 將點(diǎn)的大小改為 20 。如下代碼:
// 這里進(jìn)行所有的繪圖工作
void SceneShow ( GLvoid )
{
glClear ( GL_COLOR_BUFFER_BIT );
glPointSize (20);
glBegin ( GL_POINTS );
glVertex3f (-0.5, -0.5, 0.0);
glVertex3f (0.5, -0.5, 0.0);
glVertex3f (0.5, 0.5, 0.0);
glVertex3f (-0.5, 0.5, 0.0);
glEnd ();
glFlush ();
}
效果如附圖 3 所示。
光是點(diǎn)也可以做很多有趣的應(yīng)用, Windows 的屏保模擬星空即是其一,事實(shí)上,在原來(lái)學(xué)習(xí)簡(jiǎn)單的圖形編程時(shí),我用多種語(yǔ)言嘗試了用點(diǎn)做一些有意思的東西。見簡(jiǎn)單圖形編程學(xué)習(xí)系列:
《 簡(jiǎn)單圖形編程的學(xué)習(xí)( 2 ) --- 點(diǎn) (Qt 實(shí)現(xiàn) ) 》
《 簡(jiǎn)單圖形編程的學(xué)習(xí)( 2 ) --- 點(diǎn) (Windows GDI 實(shí)現(xiàn) ) 》
《 簡(jiǎn)單圖形編程的學(xué)習(xí)( 2 ) --- 點(diǎn) (small basic 實(shí)現(xiàn) ) 》
其中點(diǎn)與文字結(jié)合形成的那個(gè)星空文字效果我是印象深刻,我一直將其作為“簡(jiǎn)單的編程技術(shù) + 創(chuàng)意”也能很強(qiáng)大的例子。
2. 直線
直線也是很基礎(chǔ)的東西了,但是 OpenGL 中的直線與數(shù)學(xué)概念上有些區(qū)別,不知道外國(guó)的那些專家在命名時(shí)為啥亂了,其實(shí) OpenGL 中的直線與數(shù)學(xué)中的線段概念一致,有兩個(gè)端點(diǎn)確認(rèn)長(zhǎng)度。事實(shí)上,在 OpenGL 繪制中,指定的也就是線段的兩個(gè)端點(diǎn)。用 GL_LINES 調(diào)用 glBegin 時(shí),表示繪制的是直線。默認(rèn)情況下寬度為一個(gè)像素,同樣, OpenGL 也提供了函數(shù) glLineWidth 用以改變直線的寬度。比如上例中,僅僅將 GL_QUADS 換成 GL_LINES 將是兩條平行的直線,并用 glLineWidth 將直線寬度改為 5 ,如下代碼:
// 這里進(jìn)行所有的繪圖工作
void SceneShow ( GLvoid )
{
glClear ( GL_COLOR_BUFFER_BIT );
glLineWidth (5);
glBegin ( GL_LINES );
glVertex3f (-0.5, -0.5, 0.0);
glVertex3f (0.5, -0.5, 0.0);
glVertex3f (0.5, 0.5, 0.0);
glVertex3f (-0.5, 0.5, 0.0);
glEnd ();
glFlush ();
}
運(yùn)行效果如附圖 4 所示。
直線的應(yīng)用也是非常廣泛的,還記得 Windows 的屏保中的變幻線嗎?千變?nèi)f變,無(wú)論多么絢爛的效果,其實(shí)也僅僅是一條條變化的線段而已。
3. 多邊形
其實(shí)可以將多邊形看成是從點(diǎn)到線到面的一種擴(kuò)展,這里的面自然也是有范圍的,那么就成了多邊形了,在繪制圖形中用的最多的是三角形,因?yàn)槿切慰隙ㄔ谕粋€(gè)面上,這樣可以簡(jiǎn)化很多計(jì)算的處理。 GL_TRIANGLES 調(diào)用 glBegin 表示開始繪制三角形。然后還有 GL_QUADS 表示四邊形(上例中的用法), GL_POLYGON 表示多邊形(必須是凸的)。
4. 小結(jié)
其實(shí)除了上面講到的那些參數(shù),還有一些額外的參數(shù),比如 GL_LINE_STRIP,GL_TRIANGLES_STRIP 等,表示繪制的時(shí)候繪制一些列連續(xù)的圖形,這些參數(shù)用文字解釋起來(lái)不夠形象,還是推薦參考 《 OpenGL Programming Guide 》的 此頁(yè) 中 Figure 2-7 : Geometric Primitive Types 一圖,此圖形象的展示了各個(gè)參數(shù)時(shí)對(duì)點(diǎn)的解釋方式和順序。在此圖的下面,還有個(gè)表總結(jié)了一下各個(gè)參數(shù)的作用。
在一個(gè) glBegin 和 glEnd 對(duì)中可以連續(xù)的制定多個(gè)頂點(diǎn),甚至超出你指定的圖形的數(shù)目,比如繪制三角形時(shí)可以指定 6 個(gè)點(diǎn),那么此時(shí),將會(huì)繪制兩個(gè)三角形而不是一個(gè),這樣而當(dāng)指定 4 , 5 個(gè)點(diǎn)時(shí)用 GL_TRIANGLES 參數(shù)時(shí)將會(huì)丟棄,用 GL_TRIANGLE_STRIP 參數(shù)時(shí)將會(huì)連續(xù)繪制兩個(gè)三角形,具體的解釋方法也是要看 glBegin 參數(shù)的,建議還是參考上述圖片。
五、 參考資料
1. 《 OpenGL Reference Manual 》, OpenGL 參考手冊(cè)
2. 《 OpenGL 編程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波譯,機(jī)械工業(yè)出版社
3. 《 Nehe OpenGL Tutorials 》, Nehe 著,在 http://nehe.gamedev.net/ 上可以找到教程及相關(guān)的代碼下載,(有 PDF 版本教程下載) Nehe 自己還做了一個(gè)面向?qū)ο蟮目蚣埽鳛檠菔境绦騺?lái)說(shuō),這樣的框架非常合適。也有 中文版 ,各取所需吧。
4. 《 OpenGL 入門學(xué)習(xí)》 , eastcowboy 著,這是我在網(wǎng)上找到的一個(gè)比較好的教程,較為完善,而且非常通俗。這是第一篇的地址: http://bbs.pfan.cn/post-184355.html
六、 系列其他文章
1. 《 Win32 OpenGL 編程(1)Win32下的OpenGL編程必須步驟 》
2. 《 Win32 OpenGL編程(2) 尋找缺失的OpenGL函數(shù) 》
七、 最后說(shuō)明
本文中所有代碼(如果有的話)都能用 Mercurial 在 Google Code 中下載。
文章以博文發(fā)表的日期分目錄存放,下載地址見:
http://code.google.com/p/jtianling/source/checkout?repo=blog-sample-code
或者直接使用 Mercurial 克隆下列庫(kù):
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial 使用方法見《 分布式的,新一代 版本控制系統(tǒng) Mercurial 的介紹及簡(jiǎn)要入門 》
八、 附圖
1. 圖 1 SimpleRectangle
圖2 SimpleRectangle with coordinate
圖3 SimplePoints
圖4 SimpleLines
write by 九天雁翎 (JTianLing) -- blog.csdn.net/vagrxie
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
