opengl實(shí)現(xiàn)直線掃描算法和區(qū)域填充算法
本文實(shí)例為大家分享了opengl實(shí)現(xiàn)直線掃描算法和區(qū)域填充算法,供大家參考,具體內(nèi)容如下
總體介紹
1、采用直線掃描算法繪制一條線段,直線由離散點(diǎn)組成
2、利用區(qū)域填充算法繪制多邊形區(qū)域,區(qū)域由離散點(diǎn)組成
開發(fā)環(huán)境VS2012+OpenGL
開發(fā)平臺 Intel core i5,Intel HD Graphics Family
設(shè)計(jì)思路
一、直線掃描算法
1、數(shù)值微分法(DDA)
已知過端點(diǎn)P0 (x0, y0), P1(x1, y1)的直線段L:y = kx + b,容易得知直線斜率為:k = (y1-y0)/(x1-x0),(假設(shè)x1≠x0)。
我們假設(shè)|k|≤1,這樣x每增加1,y將增加k,并且保證x每增加1,y的增量不能大于1;如果|k| > 1,則應(yīng)該將x和y互換。由于k是浮點(diǎn)數(shù),因此算法中需要將y舍入為int型,并圓整到最接近的位置。
DDA算法在每次迭代中的x, y值是上一步的值加上一個增量獲得的,因此它是一個增量算法。但是這種方法直觀,但效率太低,因?yàn)槊恳徊叫枰淮胃↑c(diǎn)乘法和一次舍入運(yùn)算。
2、中點(diǎn)畫線法
在直線斜率在0~1直接的情況下,設(shè)當(dāng)前像素點(diǎn)為(x,y),那么它的下一個像素點(diǎn)就是p1(x+1,y)或者p2(x+1,y+1),若稱p1和p2的中點(diǎn)M(px+1,y+0.5),Q為理想直線與x+1垂線的交點(diǎn),當(dāng)Q在M的下方時,p1即為下一個像素點(diǎn),否則p2即為下一個像素點(diǎn)。
3、Bresenham算法
過各行各列象素中心構(gòu)造一組虛擬網(wǎng)格線。按直線從起點(diǎn)到終點(diǎn)的順序計(jì)算直線與各垂直網(wǎng)格線的交點(diǎn),然后確定該列象素中與此交點(diǎn)最近的象素。該算法的巧妙之處在于采用增量計(jì)算,使得對于每一列,只要檢查一個誤差項(xiàng)的符號,就可以確定該列的所求象素。

如圖所示,設(shè)直線方程為yi+1=yi+k(xi+1-xi)+k。假設(shè)列坐標(biāo)象素已經(jīng)確定為xi,其行坐標(biāo)為yi。那么下一個象素的列坐標(biāo)為xi+1,而行坐標(biāo)要么為yi,要么遞增1為yi+1。是否增1取決于誤差項(xiàng)d的值。誤差項(xiàng)d的初值d0=0,x坐標(biāo)每增加1,d的值相應(yīng)遞增直線的斜率值k,即d=d+k。一旦 d≥1,就把它減去1,這樣保證d在0、1之間。當(dāng)d≥0.5時,直線與垂線x=xi+1交點(diǎn)最接近于當(dāng)前象素(xi,yi)的右上方象素(xi+1,yi+1);而當(dāng)d<0.5時,更接近于右方象素(xi+1,yi)。為方便計(jì)算,令e=d-0.5,e的初值為-0.5,增量為k。當(dāng)e≥0時,取當(dāng)前象素(xi,yi)的右上方象素(xi+1,yi+1);而當(dāng)e<0時,取(xi,yi)右方象素(xi+1,yi)。
二、區(qū)域填充算法
1、遞歸算法
從指定的種子點(diǎn)開始,向各個方向上搜索,逐個像素進(jìn)行處理,直到遇到邊界,各種種子填充算法只是在處理顏色和邊界的方式上有所不同。
2、掃描線算法
掃描線種子填充算法的基本過程如下:當(dāng)給定種子點(diǎn)(x, y)時,首先分別向左和向右兩個方向填充種子點(diǎn)所在掃描線上的位于給定區(qū)域的一個區(qū)段,同時記下這個區(qū)段的范圍[xLeft, xRight],然后確定與這一區(qū)段相連通的上、下兩條掃描線上位于給定區(qū)域內(nèi)的區(qū)段,并依次保存下來。反復(fù)這個過程,直到填充結(jié)束。
掃描線種子填充算法可由下列四個步驟實(shí)現(xiàn):
(1) 初始化一個空的棧用于存放種子點(diǎn),將種子點(diǎn)(x, y)入棧;
(2) 判斷棧是否為空,如果棧為空則結(jié)束算法,否則取出棧頂元素作為當(dāng)前掃描線的種子點(diǎn)(x, y),y是當(dāng)前的掃描線;
(3) 從種子點(diǎn)(x,y)出發(fā),沿當(dāng)前掃描線向左、右兩個方向填充,直到邊界。分別標(biāo)記區(qū)段的左、右端點(diǎn)坐標(biāo)為xLeft和xRight;
(4) 分別檢查與當(dāng)前掃描線相鄰的y - 1和y + 1兩條掃描線在區(qū)間[xLeft,xRight]中的像素,從xLeft開始向xRight方向搜索,若存在非邊界且未填充的像素點(diǎn),則找出這些相鄰的像素點(diǎn)中最右邊的一個,并將其作為種子點(diǎn)壓入棧中,然后返回第(2)步;
三、算法實(shí)現(xiàn)



總結(jié)及學(xué)習(xí)感悟
在學(xué)習(xí)直線掃描算法時,一開始總是畫不出來,后來發(fā)現(xiàn)這句glBegin(GL_POINTS);少了個S,沒有S就只能畫一個點(diǎn),細(xì)節(jié)很重要。
學(xué)習(xí)區(qū)域填充算法時,基本的思路就是以一個點(diǎn)為起點(diǎn),不斷探索周圍,如果在這個區(qū)域內(nèi),就填充顏色,如果遇到邊界就停止。掃描線算法也是,先以某點(diǎn)畫一條直線,在區(qū)域內(nèi)的線段部分就填充顏色。
我們就像被選中的一點(diǎn)一樣,周圍的一切對我們來說都是不可知的黑色,只有不斷探索,才知道哪里是邊界,也可能或許沒有邊界,或許邊界的那邊又是一個更大的新世界······噗,我想多了。
源代碼
掃描線主要算法
void k1() //0<k<1
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
glBegin(GL_POINTS);
GLint x1=0,y1=0,x2=400,y2=200;
GLint x=x1,y=y1;
GLint dx=x2-x1,dy=y2-y1,dT=2*(dy-dx),dS=2*dy;
GLint d=2*dy-dx;
glVertex2i(x,y);
while(x<x2)
{
x++;
if(d<0)
d=d+dS;
else
{
y++;
d=d+dT;
}
glVertex2i(x,y);
}
glEnd();
glFlush();
}
區(qū)域填充
#include "gl/glut.h"
#include "windows.h"
const int POINTNUM=7; //多邊形點(diǎn)數(shù).
/******定義結(jié)構(gòu)體用于活性邊表AET和新邊表NET***********************************/
typedef struct XET
{
float x;
float dx,ymax;
XET* next;
}AET,NET;
/******定義點(diǎn)結(jié)構(gòu)體point******************************************************/
struct point
{
float x;
float y;
}
polypoint[POINTNUM]={250,50,550,150,550,400,250,250,100,350,100,100,120,30};//多邊形頂點(diǎn)
void PolyScan()
{
/******計(jì)算最高點(diǎn)的y坐標(biāo)(掃描到此結(jié)束)****************************************/
int MaxY=0;
int i;
for(i=0;i<POINTNUM;i++)
if(polypoint[i].y>MaxY)
MaxY=polypoint[i].y;
/*******初始化AET表***********************************************************/
AET *pAET=new AET;
pAET->next=NULL;
/******初始化NET表************************************************************/
NET *pNET[1024];
for(i=0;i<=MaxY;i++)
{
pNET[i]=new NET;
pNET[i]->next=NULL;
}
glClear(GL_COLOR_BUFFER_BIT); //賦值的窗口顯示.
glColor3f(0.0,0.0,0.0); //設(shè)置直線的顏色紅色
glBegin(GL_POINTS);
/******掃描并建立NET表*********************************************************/
for(i=0;i<=MaxY;i++)
{
for(int j=0;j<POINTNUM;j++)
if(polypoint[j].y==i)
{ //一個點(diǎn)跟前面的一個點(diǎn)形成一條線段,跟后面的點(diǎn)也形成線段
if(polypoint[(j-1+POINTNUM)%POINTNUM].y>polypoint[j].y)
{
NET *p=new NET;
p->x=polypoint[j].x;
p->ymax=polypoint[(j-1+POINTNUM)%POINTNUM].y;
p->dx=(polypoint[(j-1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j-1+POINTNUM)%POINTNUM].y-polypoint[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p;
}
if(polypoint[(j+1+POINTNUM)%POINTNUM].y>polypoint[j].y)
{
NET *p=new NET;
p->x=polypoint[j].x;
p->ymax=polypoint[(j+1+POINTNUM)%POINTNUM].y;
p->dx=(polypoint[(j+1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j+1+POINTNUM)%POINTNUM].y-polypoint[j].y);
p->next=pNET[i]->next;
pNET[i]->next=p;
}
}
}
/******建立并更新活性邊表AET*****************************************************/
for(i=0;i<=MaxY;i++)
{
//計(jì)算新的交點(diǎn)x,更新AET
NET *p=pAET->next;
while(p)
{
p->x=p->x + p->dx;
p=p->next;
}
//更新后新AET先排序*************************************************************/
//斷表排序,不再開辟空間
AET *tq=pAET;
p=pAET->next;
tq->next=NULL;
while(p)
{
while(tq->next && p->x >= tq->next->x)
tq=tq->next;
NET *s=p->next;
p->next=tq->next;
tq->next=p;
p=s;
tq=pAET;
}
//(改進(jìn)算法)先從AET表中刪除ymax==i的結(jié)點(diǎn)****************************************/
AET *q=pAET;
p=q->next;
while(p)
{
if(p->ymax==i)
{
q->next=p->next;
delete p;
p=q->next;
}
else
{
q=q->next;
p=q->next;
}
}
//將NET中的新點(diǎn)加入AET,并用插入法按X值遞增排序**********************************/
p=pNET[i]->next;
q=pAET;
while(p)
{
while(q->next && p->x >= q->next->x)
q=q->next;
NET *s=p->next;
p->next=q->next;
q->next=p;
p=s;
q=pAET;
}
/******配對填充顏色***************************************************************/
p=pAET->next;
while(p && p->next)
{
for(float j=p->x;j<=p->next->x;j++)
glVertex2i(static_cast<int>(j),i);
p=p->next->next;//考慮端點(diǎn)情況
}
}
glEnd();
glFlush();
}
void init(void)
{glClearColor(1.0,1.0,1.0,0.0);
//窗口的背景顏色設(shè)置為白色
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0,600.0,0.0,450.0);
}
void main(int argc,char* argv)
{
glutInit(&argc,&argv); //I初始化 GLUT.
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //設(shè)置顯示模式:單個緩存和使用RGB模型
glutInitWindowPosition(50,100); //設(shè)置窗口的頂部和左邊位置
glutInitWindowSize(400,300); //設(shè)置窗口的高度和寬度
glutCreateWindow("An Example OpenGL Program"); //創(chuàng)建顯示窗口
init(); //調(diào)用初始化過程
glutDisplayFunc(PolyScan); //圖形的定義傳遞給我window.
glutMainLoop(); //顯示所有的圖形并等待
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用C++中string實(shí)現(xiàn)任意長度的正小數(shù)、整數(shù)之間加減法方法實(shí)例
這篇文章主要介紹了利用C++中string函數(shù)實(shí)現(xiàn)任意長度的正小數(shù)、整數(shù)之間加減法方法實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-06-06
C++利用GPAC實(shí)現(xiàn)生成MP4文件的示例代碼
GPAC主要針對學(xué)生和內(nèi)容創(chuàng)作者,代表了一個跨平臺的多媒體框架,開發(fā)人員可以使用它在?LGPL?許可下制作開源媒體。本文就來用GPAC實(shí)現(xiàn)生成MP4文件,感興趣的可以了解一下2023-02-02
QT編寫窗口插件實(shí)現(xiàn)調(diào)用窗口的自適應(yīng)
這篇文章主要為大家詳細(xì)介紹了QT編寫窗口插件實(shí)現(xiàn)調(diào)用窗口的自適應(yīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-06-06
C++中CSimpleList的實(shí)現(xiàn)與測試實(shí)例
這篇文章主要介紹了C++中CSimpleList的實(shí)現(xiàn)與測試實(shí)例,較為詳細(xì)的講述了C++列表類的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-10-10
C++編程中的const關(guān)鍵字常見用法總結(jié)
這篇文章主要介紹了C++編程中的const關(guān)鍵字常見用法總結(jié),const關(guān)鍵字的使用是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-11-11

