OpenGL入門學習(三) 【轉】

 在第二課中,我們學習瞭如何繪製幾何圖形,但大家如果多寫幾個程序,就會發現其實還是有些鬱悶之處。例如:點太小,難以看清楚;直線也太細,不舒服;或者想畫虛線,但不知道方法只能用許多短直線,甚至用點組合而成。
這些問題將在本課中被解決。

下面就點、直線、多邊形分別討論。


1、關於點
點的大小默認爲1個像素,但也可以改變之。改變的命令爲glPointSize,其函數原型如下:
void glPointSize(GLfloat size);
size必須大於0.0f,默認值爲1.0f,單位爲“像素”。
注意:對於具體的OpenGL實現,點的大小都有個限度的,如果設置的size超過最大值,則設置可能會有問題。
例子:
void myDisplay(void)
{
     glClear(GL_COLOR_BUFFER_BIT);
     glPointSize(5.0f);
     glBegin(GL_POINTS);
         glVertex2f(0.0f, 0.0f);
         glVertex2f(0.5f, 0.5f);
     glEnd();
     glFlush();
}

2、關於直線
(1)直線可以指定寬度:
void glLineWidth(GLfloat width);
其用法跟glPointSize類似。
(2)畫虛線。
首先,使用glEnable(GL_LINE_STIPPLE);來啓動虛線模式(使用glDisable(GL_LINE_STIPPLE)可以關閉之)。
然後,使用glLineStipple來設置虛線的樣式。
void glLineStipple(GLint factor, GLushort pattern);
pattern是由1和0組成的長度爲16的序列,從最低位開始看,如果爲1,則直線上接下來應該畫的factor個點將被畫爲實的;如果爲0,則直線上接下來應該畫的factor個點將被畫爲虛的。
以下是一些例子:
http://blog.programfan.com/upfile/200608/20060801172519.gif
聲明:該圖片來自www.opengl.org,該圖片是《OpenGL編程指南》一書的附圖,由於該書的舊版(第一版,1994年)已經流傳於網絡,我希望沒有觸及到版權問題。
示例代碼:
void myDisplay(void)
{
     glClear(GL_COLOR_BUFFER_BIT);
     glEnable(GL_LINE_STIPPLE);
     glLineStipple(2, 0x0F0F);
     glLineWidth(10.0f);
     glBegin(GL_LINES);
         glVertex2f(0.0f, 0.0f);
         glVertex2f(0.5f, 0.5f);
     glEnd();
     glFlush();
}


3、關於多邊形
多邊形的內容較多,我們將講述以下四個方面。
(1)多邊形的兩面以及繪製方式。
雖然我們目前還沒有真正的使用三維座標來畫圖,但是建立一些三維的概念還是必要的。
從三維的角度來看,一個多邊形具有兩個面。每一個面都可以設置不同的繪製方式:填充、只繪製邊緣輪廓線、只繪製頂點,其中“填充”是默認的方式。可以爲兩個面分別設置不同的方式。
glPolygonMode(GL_FRONT, GL_FILL);            // 設置正面爲填充方式
glPolygonMode(GL_BACK, GL_LINE);             // 設置反面爲邊緣繪製方式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 設置兩面均爲頂點繪製方式


(2)反轉
一般約定爲“頂點以逆時針順序出現在屏幕上的面”爲“正面”,另一個面即成爲“反面”。生活中常見的物體表面,通常都可以用這樣的“正面”和“反面”,“合理的”被表現出來(請找一個比較透明的礦泉水瓶子,在正對你的一面沿逆時針畫一個圓,並標明畫的方向,然後將背面轉爲正面,畫一個類似的圓,體會一下“正面”和“反面”。你會發現正對你的方向,瓶的外側是正面,而背對你的方向,瓶的內側纔是正面。正對你的內側和背對你的外側則是反面。這樣一來,同樣屬於“瓶的外側”這個表面,但某些地方算是正面,某些地方卻算是反面了)。
但也有一些表面比較特殊。例如“麥比烏斯帶”(請自己Google一下),可以全部使用“正面”或全部使用“背面”來表示。
可以通過glFrontFace函數來交換“正面”和“反面”的概念。
glFrontFace(GL_CCW);   // 設置CCW方向爲“正面”,CCW即CounterClockWise,逆時針
glFrontFace(GL_CW);    // 設置CW方向爲“正面”,CW即ClockWise,順時針
下面是一個示例程序,請用它替換第一課中的myDisplay函數,並將glFrontFace(GL_CCW)修改爲glFrontFace(GL_CW),並觀察結果的變化。
void myDisplay(void)
{
     glClear(GL_COLOR_BUFFER_BIT);
     glPolygonMode(GL_FRONT, GL_FILL); // 設置正面爲填充模式
     glPolygonMode(GL_BACK, GL_LINE);   // 設置反面爲線形模式
     glFrontFace(GL_CCW);               // 設置逆時針方向爲正面
     glBegin(GL_POLYGON);               // 按逆時針繪製一個正方形,在左下方
         glVertex2f(-0.5f, -0.5f);
         glVertex2f(0.0f, -0.5f);
         glVertex2f(0.0f, 0.0f);
         glVertex2f(-0.5f, 0.0f);
     glEnd();
     glBegin(GL_POLYGON);               // 按順時針繪製一個正方形,在右上方
         glVertex2f(0.0f, 0.0f);
         glVertex2f(0.0f, 0.5f);
         glVertex2f(0.5f, 0.5f);
         glVertex2f(0.5f, 0.0f);
     glEnd();
     glFlush();
}
(3)剔除多邊形表面
在三維空間中,一個多邊形雖然有兩個面,但我們無法看見背面的那些多邊形,而一些多邊形雖然是正面的,但被其他多邊形所遮擋。如果將無法看見的多邊形和可見的多邊形同等對待,無疑會降低我們處理圖形的效率。在這種時候,可以將不必要的面剔除。
首先,使用glEnable(GL_CULL_FACE);來啓動剔除功能(使用glDisable(GL_CULL_FACE)可以關閉之)
然後,使用glCullFace來進行剔除。
glCullFace的參數可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分別表示剔除正面、剔除反面、剔除正反兩面的多邊形。
注意:剔除功能隻影響多邊形,而對點和直線無影響。例如,使用glCullFace(GL_FRONT_AND_BACK)後,所有的多邊形都將被剔除,所以看見的就只有點和直線。

(4)鏤空多邊形
直線可以被畫成虛線,而多邊形則可以進行鏤空。
首先,使用glEnable(GL_POLYGON_STIPPLE);來啓動鏤空模式(使用glDisable(GL_POLYGON_STIPPLE)可以關閉之)。
然後,使用glPolygonStipple來設置鏤空的樣式。
void glPolygonStipple(const GLubyte *mask);
其中的參數mask指向一個長度爲128字節的空間,它表示了一個32*32的矩形應該如何鏤空。其中:第一個字節表示了最左下方的從左到右(也可以是從右到左,這個可以修改)8個像素是否鏤空(1表示不鏤空,顯示該像素;0表示鏤空,顯示其後面的顏色),最後一個字節表示了最右上方的8個像素是否鏤空。
但是,如果我們直接定義這個mask數組,像這樣:
static GLubyte Mask[128] =
{
     0x00, 0x00, 0x00, 0x00,    //   這是最下面的一行
     0x00, 0x00, 0x00, 0x00,
     0x03, 0x80, 0x01, 0xC0,    //  
     0x06, 0xC0, 0x03, 0x60,    //  
     0x04, 0x60, 0x06, 0x20,    //  
     0x04, 0x30, 0x0C, 0x20,    //  
     0x04, 0x18, 0x18, 0x20,    //  
     0x04, 0x0C, 0x30, 0x20,    //  
     0x04, 0x06, 0x60, 0x20,    //  
     0x44, 0x03, 0xC0, 0x22,    //  
     0x44, 0x01, 0x80, 0x22,    //  
     0x44, 0x01, 0x80, 0x22,    //  
     0x44, 0x01, 0x80, 0x22,    //  使
     0x44, 0x01, 0x80, 0x22,    //  
     0x44, 0x01, 0x80, 0x22,
     0x44, 0x01, 0x80, 0x22,
     0x66, 0x01, 0x80, 0x66,
     0x33, 0x01, 0x80, 0xCC,
     0x19, 0x81, 0x81, 0x98,
     0x0C, 0xC1, 0x83, 0x30,
     0x07, 0xE1, 0x87, 0xE0,
     0x03, 0x3F, 0xFC, 0xC0,
     0x03, 0x31, 0x8C, 0xC0,
     0x03, 0x3F, 0xFC, 0xC0,
     0x06, 0x64, 0x26, 0x60,
     0x0C, 0xCC, 0x33, 0x30,
     0x18, 0xCC, 0x33, 0x18,
     0x10, 0xC4, 0x23, 0x08,
     0x10, 0x63, 0xC6, 0x08,
     0x10, 0x30, 0x0C, 0x08,
     0x10, 0x18, 0x18, 0x08,
     0x10, 0x00, 0x00, 0x08    // 這是最上面的一行
};
這樣一堆數據非常缺乏直觀性,我們需要很費勁的去分析,纔會發現它表示的竟然是一隻蒼蠅。
如果將這樣的數據保存成圖片,並用專門的工具進行編輯,顯然會方便很多。下面介紹如何做到這一點。


首先,用Windows自帶的畫筆程序新建一副圖片,取名爲mask.bmp,注意保存時,應該選擇“單色位圖”。在“圖象”->“屬性”對話框中,設置圖片的高度和寬度均爲32。
用放大鏡觀察圖片,並編輯之。黑色對應二進制零(鏤空),白色對應二進制一(不鏤空),編輯完畢後保存。
然後,就可以使用以下代碼來獲得這個Mask數組了。
static GLubyte Mask[128];
FILE *fp;
fp = fopen("mask.bmp", "rb");
if( !fp )
     exit(0);
// 移動文件指針到這個位置,使得再讀sizeof(Mask)個字節就會遇到文件結束
// 注意-(int)sizeof(Mask)雖然不是什麼好的寫法,但這裏它確實是正確有效的
// 如果直接寫-sizeof(Mask)的話,因爲sizeof取得的是一個無符號數,取負號會有問題
if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )
     exit(0);
// 讀取sizeof(Mask)個字節到Mask
if( !fread(Mask, sizeof(Mask), 1, fp) )
     exit(0);
fclose(fp);


好的,現在請自己編輯一個圖片作爲mask,並用上述方法取得Mask數組,運行後觀察效果。
說明:繪製虛線時可以設置factor因子,但多邊形的鏤空無法設置factor因子。請用鼠標改變窗口的大小,觀察鏤空效果的變化情況。
#include <stdio.h>
#include <stdlib.h>
void myDisplay(void)
{
     static GLubyte Mask[128];
     FILE *fp;
     fp = fopen("mask.bmp", "rb");
     if( !fp )
         exit(0);
     if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )
         exit(0);
     if( !fread(Mask, sizeof(Mask), 1, fp) )
         exit(0);
     fclose(fp);
     glClear(GL_COLOR_BUFFER_BIT);
     glEnable(GL_POLYGON_STIPPLE);
     glPolygonStipple(Mask);
     glRectf(-0.5f, -0.5f, 0.0f, 0.0f);   // 在左下方繪製一個有鏤空效果的正方形
     glDisable(GL_POLYGON_STIPPLE);
     glRectf(0.0f, 0.0f, 0.5f, 0.5f);     // 在右上方繪製一個無鏤空效果的正方形
     glFlush();
}


小結
本課學習了繪製幾何圖形的一些細節。
點可以設置大小。
直線可以設置寬度;可以將直線畫成虛線。
多邊形的兩個面的繪製方法可以分別設置;在三維空間中,不可見的多邊形可以被剔除;可以將填充多邊形繪製成鏤空的樣式。
瞭解這些細節會使我們在一些圖象繪製中更加得心應手。
另外,把一些數據寫到程序之外的文件中,並用專門的工具編輯之,有時可以顯得更方便。
發佈了8 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章