1. 程式人生 > >【計算機圖形學】圖元的區域填充之多邊形的區域填充

【計算機圖形學】圖元的區域填充之多邊形的區域填充

相關資料來源於網路,侵刪歉。
如果文章中存在錯誤,請下方評論告知我,謝謝!


多邊形的區域填充

首先,我們瞭解一下多邊形。
多邊形可以簡單地分為凸多邊形和凹多邊形,除此之外,我們還要討論內含環的多邊形,如下圖。

多邊形的表示方法
頂點表示:用多邊形頂點的序列來刻畫多邊形。直觀、幾何意義強、佔記憶體少;不能直接用於顯示。
點陣表示:用位於多邊形內的畫素的集合來刻畫多邊形。失去了許多重要的幾何資訊;但它是光柵顯示系統顯示時所需的表示形式,易於面著色。

多邊形的掃描轉換(多邊形的區域填充)
把多邊形的頂點表示轉換為點陣表示,也就是從多邊形的給定邊界出發,求出位於其內部的各個畫素,並給各個畫素設定相應的灰度和顏色,通常稱這種轉換為多邊形的掃描轉換。
方法:逐點判斷法,掃描線演算法,邊緣填充法等。


前提
多邊形的頂點座標均為整數。

逐點判斷法

逐個判斷繪圖視窗內的畫素,確定是否在多邊形區域內,從而求出位於多邊形區域內的畫素集合。

#define MAXN 100
typedef struct{
	int PolygonNum;//多邊形頂點個數
	Point vertexces[MAXN];//多邊形頂點陣列
}Polygon;

void FillPolygonPbyP(Polygon *P, int color){
	int x, y;
	for(y=ymin;y<=ymax;y++)
	for(x=xmin;x<=xmax;x++)
		if(isInside(P,x,y))
			WritePixel(x,y,color);
}

如何判斷點在多邊形內外呢?
1.射線法
從點v發出射線,求與多邊形的交點個數n。若n為奇數,則點v在多邊形內部;若n為偶數,則點v在多邊形外部。
2.累計角度法
從點v向多邊形的每個頂點發出射線,形成有向角,計算所有有向角的角度和S。若S=0,則點v在多邊形外部;若S=±2π,則點v在多邊形內部。

逐點判斷的演算法雖然程式簡單,但不可取。原因是速度太慢,主要是由於該演算法割斷了各象素之間的聯絡,孤立地考察各象素與多邊形的內外關係,使得幾十萬甚至幾百萬個象素都要一一判別,每次判別又要多次求交點,需要做大量的乘除運算,花費很多時間。

掃描線演算法

前提:處理物件為非自交多邊形,即邊與邊之間除了頂點外無其它交點,如下圖是自交多邊形。


將多邊形填充分解為一條條的掃描線填充(掃描線是縱座標為整數的橫線)。對任一條掃描線,自左向右確定該掃描線與多邊形的交點位置,並對每對交點間的畫素進行填充。那麼關鍵是如何求掃描線與多邊形各邊的交點,並進行取捨,如下圖。

我們作如下規定
1.如果交點非整數
交點位於左邊之上,向右取整;交點位於右邊之上,向左取整。(要使交點位於多邊形內)
2.落在右上邊界的畫素不記錄。
3.如果交點是與掃描線相交的那條邊的上端點,單獨對這條邊不記錄。(結合上圖中2、4、5、6-7間的點加以理解)

步驟
求交:求掃描線與多邊形各邊交點
排序:按x遞增順序對交點排序
交點匹配:依次每兩個交點配對,在上圖中(1,2),(3,4),(5,6),(7,8)配對。
填充:填充每對交點間在多邊形區域內部的象素。

幾點說明
1.交點個數為偶數。
2.若交點個數為n,則配對的點為(k,k+1),k=1,3,5...,n-1。
3.配對的區間是位於多邊形內的,其餘不是。

接下來我們具體瞭解一下如何求掃描線與多邊形的交點

假定已經求解出掃描線y=e和多邊形邊的所有交點,那麼遞推出掃描線y=d=e+1與多邊形邊的交點,求解可分為兩類:
1.邊既和y=e相交,又和y=d相交。
若x為其中某條邊與y=e的交點橫座標,k為該邊的斜率,則此邊與y=d的交點橫座標x'=x+1/k;
2.邊只與y=d相交,不與y=e相交,即新出現的邊。
此時,這些邊的下端點就是交點,不用計算。    

幾種特殊情況
1.不對水平邊計算,在預處理階段將水平邊刪除。
2.對尖角的處理(即填充區域非常狹窄),掃描線原則上一個元素都不會填充,這時我們需要進行反走樣。

好了,我們來看實現
掃描線演算法中採用了靈活的資料結構。
1.邊結構

typedef struct   
{
    int ymax; // 邊的上端點的y值
    float x; // ET表中為邊的下端點的x值;AET中為當前掃描線與邊的交點的x值
    float dx; // 單位高度x方向偏移量(即邊的斜率的倒數)
    E * nextEdge ;  // 指向下一條邊的指標
}E;

2.邊表ET
邊表ET的基本元素是邊結構,它是按多邊形每條邊的下端點的y座標對非水平邊進行分類的。邊的下端點y座標值等於i,那麼該邊就屬於第i類。同一類中的邊按其下端點的x值(x值相等的,按dx值)增序排列。如下圖。

3.活動邊表AET
活動邊表AET的基本元素也是邊結構,它由當前與掃描線相交的邊組成,記錄多邊形的邊和當前掃描線的所有交點的x座標,並且隨著掃描線的遞增而不斷變化。

(邊表ET用來排除不必要的求交測試。如求掃描線y=4的AET時,e3,e4所屬的類序號5大於4,所以它們和y=4沒有交點,不用進行求交測試。)
 

虛擬碼:

void polyfill (polygon, color)
{  
	//建立全域性邊表ET;
	for (各條掃描線i )  { 把ymin == i 的邊結構->邊表ET [i]  }
	將掃描線i的初值置為ET中非空元素的最小序號;
	初始化活動邊表AET為空;
	for (各條掃描線i )
	{
		(1) 把邊表ET[i]中的邊結點插入AET表;
		(2) 遍歷AET表,把y max== i 的結點從AET中刪除,並按x座標值增序排列各邊;
		(3) 把配對交點區間(左閉右開)上的象素(x, i),用WritePixel (x, i, color)改寫顏色值;
		(4) 把AET中每條邊結點的x值遞增△x;
	}
}