GPU 如何判斷多邊形內(nèi)部頂點是否進行渲染
1. 判斷多邊形是否為自相交圖形
如果多邊形內(nèi)部的頂點沒有重合的頂點, 基本上就代表了此多邊形為不自相交多邊形, 因此, 也就不存在多邊形內(nèi)部頂點的顏色是否渲染的問題, 如果多邊形內(nèi)部的頂點存在相交或重合頂點, 則代表內(nèi)部存在頂點需要使用其他規(guī)則進行渲染判斷.
自相交多邊形因為有相交部分, 這部分中的頂點可能不是圖形內(nèi)部的點, 頂點不是圖形內(nèi)部的點就不需要渲染, 所以就需要判斷頂點是在多邊形內(nèi)部還是外部, 判斷的規(guī)則有兩種.
判斷多邊形是否為自相交圖形需要使用具體的算法進行判斷, 上面的結(jié)論為一般結(jié)論, 所以, 實際使用時需要視情況而定.
2. 自相交的多邊形如何確定頂點渲染顏色, 采用 非零環(huán)繞數(shù)規(guī)則 或者 奇偶規(guī)則
- 注意: 此處的多邊形路徑一定是同一條路徑形成的多邊形, 例如 Path 對象中可以不斷的增加路徑都會被視為多邊形內(nèi)部邊, 如果繪制兩個 path 則其內(nèi)部會獨立計算.
非零環(huán)繞數(shù) ___> 非零頂點為外點
此規(guī)則采用的是計算經(jīng)過的 path 以及對應方向的方式確定點的內(nèi)部外部, 最后決定是否對部分頂點進行渲染.
-
具體計算方式
- 將多邊形的最外層頂點看做是一條路徑( 事實上本身也應該是一條路徑 ), 同時規(guī)定好多邊形外層頂點的運行方向
- 從需要計算的頂點處引出一條任意方向射線, 此時環(huán)繞數(shù)初始化為 0 , 然后計算此射線經(jīng)過的外層頂點形成的路徑, 如果經(jīng)過順時針方向路徑, 則 -1 , 否則 +1 , 射線超出多邊形范圍后, 此時得到的非零環(huán)繞數(shù), 如果為 0 則代表頂點為多邊形外部頂點因此不需要渲染, 如果環(huán)繞數(shù)非零, 則頂點為內(nèi)部頂點, 需要進行渲染.
注意: 因為非零環(huán)繞數(shù)的計算方式需要使用路徑的方向, 所以, 在繪圖的時候一般都會自然的包含有方向元素, 例如繪制 Arc 時需要確定起始角度以及結(jié)束角度等.
奇偶規(guī)則 ___> 偶數(shù)頂點為外點
此規(guī)則采用的是計算射線經(jīng)過的邊的數(shù)量的奇偶性質(zhì)來決定頂點的內(nèi)外分配.
-
具體計算方式
- 同樣需要將多邊形的最外層頂點看做是一條路徑, 但是不需要規(guī)定路徑的方向.
- 從待計算頂點向外導出射線, 如果射線經(jīng)過的外邊數(shù)量為偶數(shù)則代表此頂點為外部頂點, 不需要渲染, 如果是奇數(shù), 就是內(nèi)部頂點, 需要進行渲染.
具體語言中的實際應用
iOS 的渲染處理
1.CGContextClip 使用非零環(huán)繞數(shù)規(guī)則來判斷當前路徑和裁剪路徑的交集局荚。
2.CGContextEOClip 使用奇偶環(huán)繞數(shù)規(guī)則來判斷當前路徑和裁剪路徑的交集。
實際應用: 如何繪制出一個中間鏤空的圖形, 或者說如何在整片圖形中形成一個鏤空的不渲染空洞
因為以上規(guī)則都是基于繪制圖形的路徑而存在, 因此, 默認使用路徑進行圖形的繪制
如果需要在一個正方形中保證一個圓形的空洞不渲染, 那么首先需要繪制一條路徑為正方形, 此時調(diào)用 fill 函數(shù)則會對方形中所有的頂點進行顏色繪制, 因此, 給此路徑( 多邊形 )在增加一條外邊, 從中心點開始繪制一個圓形, 此時, 方形范圍內(nèi)的所有頂點被分為兩個部分, 圓形外以及圓形內(nèi), 圓形外的頂點因為非零環(huán)繞數(shù)或者奇偶原則處理后都是多邊形內(nèi)部頂點, 因此, 都會被渲染, 但是圓形內(nèi)部的頂點因為經(jīng)過非零環(huán)繞數(shù)或者奇偶原則處理過后屬于外部頂點, 因此, 不會被渲染, 最終就形成了方形中有一個空洞的狀態(tài).
同理, 如果需要繪制中空的餅狀圖, 其實就是先繪制一個較大的圓形, 之后再繪制一個較小的圓形, 兩個圓形的邊同屬一個 path 就能保證按規(guī)則計算后出現(xiàn)鏤空效果.
* iOS 中繪制一個鏤空蒙層
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[path appendPath:[UIBezierPath bezierPathWithArcCenter:self.firstPoint radius:self.radiu/2 startAngle:0 endAngle:2*M_PI clockwise:NO]];
self.maskLayer = [[CAShapeLayer alloc]init];
self.maskLayer.path = path.CGPath;
self.layer.mask = self.maskLayer;