程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> opengl基礎學習專題 (三) 多邊形繪制的幾種樣式,opengl專題

opengl基礎學習專題 (三) 多邊形繪制的幾種樣式,opengl專題

編輯:關於C語言

opengl基礎學習專題 (三) 多邊形繪制的幾種樣式,opengl專題


題外話

聰明人之所以不會成功,是由於他們缺乏堅韌的毅力。

                                                            ——艾薩克·牛頓(1643年1月4日—1727年3月31日)英國

 

 

 

          

 

 

    

  也許可以理解為 想更深一步的時候,堅持,努力和聰明缺一不可. 挺直腰桿在此向您致敬,願您仍在天國

仍潇灑的思索著,奔跑著.

 

正文

  在第二課中,我們學習了如何繪制幾何圖形,但大家如果多寫幾個程序,就會發現其實還是有些郁悶之處。

例如:點太小,難以看清楚;直線也太細,不舒服;或者想畫虛線,但不知道方法只能用許多短直線,甚至用點組合而成。

這些問題將在本課中被解決。

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

1、關於點

點的大小默認為1個像素,但也可以改變之。改變的命令為glPointSize,其函數原型如下:

void glPointSize (GLfloat size);

size必須大於0.0f,默認值為1.0f,單位為“像素”。

(注意:對於具體的OpenGL實現,點的大小都有個限度的,如果設置的size超過最大值,則設置可能會有問題。)

這裡順帶說一下 關於上面函數編譯器行為,以Window 代碼為例解釋如下

//具體的函數原型聲明
WINGDIAPI void APIENTRY glPointSize (GLfloat size);

/*
 *APIENTRY 是設置編譯器行為的宏 本質是 __stdcall
 *
 *__stdcall是函數調用約定的一種,函數調用約定主要約束了兩件事:
 *1.參數傳遞順序
 *2.調用堆棧由誰(調用函數或被調用函數)清理
 *3.函數名(在編譯器這個層次)自動加前導的下劃線,後面緊跟一個@符號,其後緊跟著參數的尺寸
 *
 *C中函數缺省默認是 __cdecl
 *每一個調用它的函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用_stdcall函數的大。
 *函數采用從右到左的壓棧方式。
 *注意:對於可變參數的成員函數,始終使用__cdecl的轉換方式。
 */
#define WINAPI      __stdcall
#define WINAPIV     __cdecl
#define APIENTRY    WINAPI

 

實際使用 glPointSize例子如下:

#include <glut.h>

/*
 *
 *這裡是繪制的主函數
 */
static void __display(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個點將被畫為虛的。

再細說一點

glEnable(GL_LINE_STIPPLE);
glLineStipple(1, Ox3F07);
//此時模式為Ox3F07(二進制形式為0011111100000111).
//它所畫出來的直線是這樣的:
//先連續繪制3個像素,然後連續5個像素留空,再連續繪制6個像素,最後兩個像素留空(注意,首先是從低位開始的)。
//如果factor是2,那麼這個模式便被擴展為:
//先連續繪制6個像素,然後連續10個像素留空,再連續繪制12個像素,最後4個像素留空。
//如果沒有啟用點畫線功能,OpenGL會自動把pattern當做為OxFFFF,把factor當成1。

更詳細的例子代碼如下:

#include <glut.h>

//繪制函數
static void __display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_LINE_STIPPLE); //開啟畫虛線模式
    glLineStipple(2,0x0F0F); //虛線的樣式
    glLineWidth(10.f);

    //畫一個線
    glBegin(GL_LINES);
    glVertex2f(0.0f,0.0f);
    glVertex2f(0.5f,0.5f);
    glEnd();

    glDisable(GL_LINE_STIPPLE); //關閉畫虛線模式
    glFlush();
}

3、關於多邊形

多邊形的內容較多,我們將講述以下四個方面。

(1)多邊形的兩面以及繪制方式。

雖然我們目前還沒有真正的使用三維坐標來畫圖,但是建立一些三維的概念還是必要的。

從三維的角度來看,一個多邊形具有兩個面。每一個面都可以設置不同的繪制方式:填充、只繪制邊緣輪廓線、只繪制頂點,其中“填充”是默認的方式。

可以為兩個面分別設置不同的方式。

glPolygonMode(GL_FRONT, GL_FILL);            // 設置正面為填充方式

glPolygonMode(GL_BACK, GL_LINE);             // 設置反面為邊緣繪制方式

glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 設置兩面均為頂點繪制方式

(2)反轉

一般約定為“頂點以逆時針順序出現在屏幕上的面”為“正面”,另一個面即成為“反面”。

生活中常見的物體表面,通常都可以用這樣的“正面”和“反面”,“合理的”被表現出來

(請找一個比較透明的礦泉水瓶子,在正對你的一面沿逆時針畫一個圓,並標明畫的方向,然後將背面轉為正面,畫一個類似的圓,體會一下“正面”和“反面”。

你會發現正對你的方向,瓶的外側是正面,而背對你的方向,瓶的內側才是正面。正對你的內側和背對你的外側則是反面。

這樣一來,同樣屬於“瓶的外側”這個表面,但某些地方算是正面,某些地方卻算是反面了)。

但也有一些表面比較特殊。例如“麥比烏斯帶”(請自己Bing一下),可以全部使用“正面”或全部使用“背面”來表示。

可以通過glFrontFace函數來交換“正面”和“反面”的概念。

glFrontFace(GL_CCW);   // 設置CCW方向為“正面”,CCW即CounterClockWise,逆時針

glFrontFace(GL_CW);    // 設置CW方向為“正面”,CW即ClockWise,順時針

下面是一個示例程序,請用它替換前面教程中的__display函數,並將glFrontFace(GL_CCW)修改為glFrontFace(GL_CW),並觀察結果的變化。

#include <glut.h>

//繪制函數
static void __display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glFrontFace(GL_CCW); // 設置逆時針方向為正面
    glPolygonMode(GL_FRONT, GL_FILL); // 設置正面為填充模式
    glPolygonMode(GL_BACK, GL_LINE); // 設置反面為線形模式

    //先逆時針繪制一個 正方形,在左下方
    glBegin(GL_POLYGON);
    glVertex2f(-0.5,-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定義 一種簡單方式 是這樣的 

win + R => mspaint + Enter => 設置畫布像素為 32*32 => 自己隨便編輯 => 保存為 my.bmp =>程序讀取這個文件 轉成 mask

下面分享一個Opengl 中 一個 老例子 如下:

#include <sc_head.h>
#include <glut.h>

static GLubyte __masks[] = {
    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    // 這是最上面的一行
};

//bmp 圖片創建函數
void bmp_create(GLubyte mask[], int len);

// 屏幕繪制函數
void display(void);


#define _STR_BMP "fly.bmp"

int main(int argc, char *argv[])
{
    //這裡先 創建 一個bmp
    bmp_create(__masks, sizeof __masks);

    glutInit(&argc,argv); // 初始化glut環境
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //設置窗口繪制為RGB模式和單緩沖模式
    glutInitWindowPosition(500,500);
    glutInitWindowSize(300,300);

    glutCreateWindow(argv[0]); //初始化啟動窗口
    glutDisplayFunc(display);

    glutMainLoop();//循環等待函數,直到glut創建的窗口關閉

    return 0;
}

//bmp 圖片創建函數
void 
bmp_create(GLubyte mask[], int len)
{
    FILE *bmp = NULL;

    if (NULL == mask || len <= 0)
        ERR_EXIT("check params error [NULL == mask || len <= 0].");

    if ((bmp = fopen(_STR_BMP, "wb")) == NULL) //練習就寫的簡單一點
        ERR_EXIT("fopen error!");
    // 文件寫入
    fwrite(mask, sizeof(GLubyte), len, bmp);

    fclose(bmp);;
}

// 屏幕繪制函數
void 
display(void)
{
    GLubyte mask[sizeof __masks];
    //先讀取文件中內容

    FILE *bmp = NULL;
    if ((bmp = fopen(_STR_BMP, "rb")) == NULL) //練習就寫的簡單一點
        ERR_EXIT("fopen error!");
    // 文件寫入
    fread(mask, sizeof(GLubyte), sizeof mask, bmp);

    fclose(bmp);

    //這裡開始繪制
    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();

}

最後的效果圖如下:

 

小結

本課學習了繪制幾何圖形的一些細節。

點可以設置大小。

直線可以設置寬度;可以將直線畫成虛線。

多邊形的兩個面的繪制方法可以分別設置;在三維空間中,不可見的多邊形可以被剔除;可以將填充多邊形繪制成镂空的樣式。

了解這些細節會使我們在一些圖象繪制中更加得心應手。

另外,把一些數據寫到程序之外的文件中,並用專門的工具編輯之,有時可以顯得更方便。

 

做為一個菜鳥,歡迎交流指正.希望此刻更有趣.加油.

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved