發表於
2015/7/4 14:40:27
1547
人閱讀
分類: opengl雜談
圖形學雜談
光柵化是在計算機上生成影象的重要步驟,然而不管是opengl還是directx還是其它的圖形介面都封裝了光柵化方法.我自己做了個光柵器,接下來就說一下如何實現光柵化的.
為什麼要光柵化?
圖形管線的輸入是圖元頂點,輸出的則是畫素(pixel),這個步驟當中還有個中間產物叫做片段(fragment),乙個片段對應乙個畫素,但片段比畫素多了用於計算的屬性,例如:深度值和法向量. 通過片段可以計算出最終將要生成畫素的顏色值,我們把輸入頂點計算片段的過程叫作光柵化.為什麼要光柵化?因為要生成用以計算最終顏色的片段.
光柵化的輸入和輸出分別是啥?
和普通函式一樣,光柵化函式也需要輸入和輸出,從之前的定義來看函式的輸入就是組成圖元的頂點結構,輸出的就是片段結構,為什麼說是結構?因為這些可以用c語言中的struct描述.
光柵化發生在哪一步?
通常在圖形介面中會暴露頂點處理程式和片段處理程式(感覺著色器聽起來也是雲裡霧裡就換成處理程式),但是這當中gpu會進行光柵化插值計算,這也就是為什麼片段處理程式的input是頂點處理程式的output經過了插值以後得到的值.既然光柵化是在頂點處理程式以後發生的步驟,那麼輸入的頂點結構是經過頂點處理以後的,也就是進行過mvp變換,乘以透視矩陣之後的頂點,注意:這步還沒有做透視除法,光柵化插值發生在裁剪空間,絕不是標準化空間,所以頂點位置是四維齊次座標不是三維座標!
怎麼實現光柵化方法?
首先我們可以確定的是光柵化的輸入和輸出分別是啥.並且應該知道手上可以是用的資料都是啥.
先對輸入的頂點進行處理變換到螢幕座標,對把裁剪空間的頂點座標轉換成標準化空間,就像這樣:
ndca.x=clipa.x/clipa.w;
ndca.y=clipa.y/clipa.w;
ndcb.x=clipb.x/clipb.w;
ndcb.y=clipb.y/clipb.w;
ndcc.x=clipc.x/clipc.w;
ndcc.y=clipc.y/clipc.w;
接著對頂點的標準座標進行視口變換:
viewporttransform(face->ndca.x,face->ndca.y,fb->width,fb->height,scrax,scray);
viewporttransform(face->ndcb.x,face->ndcb.y,fb->width,fb->height,scrbx,scrby);
viewporttransform(face->ndcc.x,face->ndcc.y,fb->width,fb->height,scrcx,scrcy);
然後得到三個二維座標代表三個頂點最終在螢幕上的位置,它們可以組成乙個二維三角形,求取三角形的包圍盒:
int minx=max(0,min(scrax,min(scrbx,scrcx)));
int maxx=min(fb->width-1,max(scrax,max(scrbx,scrcx)));
int miny=max(0,min(scray,min(scrby,scrcy)));
int maxy=min(fb->height-1,max(scray,max(scrby,scrcy)));
要注意不要超過螢幕範圍,螢幕範圍以外的點都裁剪掉.
遍歷這個包圍盒,取得潛在可能片段的螢幕位置:
for(int scrx=minx;scrx<=maxx;scrx++)
}
分別求取片段對應的標準化空間座標:
invviewporttransform(scrx,scry,fb->width,fb->height,ndcx,ndcy);
這裡用了逆視口變換,視口變換和逆視口變換很方便,只要對座標進行縮放和平移就行了.
那麼我們得到了可能片段的標準化空間的x和y座標,為什麼是可能片段呢?因為現在還沒法確定這些片段在將要被光柵化三角形的外部還是內部,我們只計算三角形內部的片段.
然而知道了這些有什麼用呢?
這邊有乙個公式可以算出三個頂點對片段產生影響的比例,也叫權值:
這個公式的a b c分別代表三角形的三個頂點, ax ay aw 分別是頂點a在裁剪空間的齊次座標(是四維的)的x y w值,這邊沒用到z值,因為z也要通過這個權值進行計算.
這個怎麼推導這個公式?
已知待光柵化三角形abc的三個頂點在裁剪空間的齊次座標,把權值alpha beta gamma設為pa pb pc,可得每個片段的裁剪空間齊次座標為:
x=pa*ax+pb*bx+pc*cx
y=pa*ay+pb*by+pc*cy
z=pa*az+pb*bz+pc*cz
w=pa*aw+pb*bw+pc*cw
然後計算片段在標準化座標系的座標值為:
nx=x/w
ny=y/w
nz=z/w
nw=1
可以推得:
x=w*nx
y=w*ny
w=w因為:
x=pa*ax+pb*bx+pc*cx
y=pa*ay+pb*by+pc*cy
w=pa*aw+pb*bw+pc*cw
轉換為3x3矩陣就是
ax bx cx pa w*nx
ay by cy * pb = w*ny
aw bw cw pc w
其中nx和ny就是之前取得的片段在標準化座標系的x y值;並且由於pa pb pc是比值,所以w可以去除;這樣只要求取3x3矩陣的逆就可以取得pa pb pc的值.
但是要注意pa+pb+pc=1,所以計算出值以後要進行如下處理:
float sum=pa+pb+pc;
pa/=sum; pb/=sum; pc/=sum;
然後把有比值小於0的片段拋棄:
if(pa<0||pb<0||pc<0)
continue;
接下來就可以用這三個權值對頂點屬性進行插值運算了.
具體的光柵化函式是這樣:
void rasterize(framebuffer* fb,depthbuffer* db,fragmentshader fs,face* face)
}}
光柵化完成了,這下就能自己實現opengl和directx了! 透視校正插值
當圖形處理器在螢幕上顯示乙個三角形時,是需要進行逐行的光柵化。三角形頂點除了含有位置資訊外,還包含顏色 紋理等屬性資訊,當然這類資訊也需要進行插值。由於投影平面上的相同步長隨著三角形面與相機距離的增加而在三角形面上產生更大的步長。圖形處理需要採用非線性插值方式來計算相關屬性資訊才能獲得正確的結果。如...
透視校正插值
在投影變換視口變換後,需要對投影到螢幕上的平面三角形頂點屬性進行線性插值,例如顏色,紋理,深度等。但對於投影前在3d空間的三角形來說,這個插值並不是線性的。下面來推導一下這個插值。假設螢幕空間的三角形的三個頂點分別為 v 0,v 1,v 2 v p 是位於三角形一邊上的點 v 0 t v 1 v 0...
透視校正插值 OpenGL
在3d渲染中,輸入資料是一些primitive資訊,包括頂點位置 顏色 紋理座標等等。在光柵化階段,primitive 一般為三角形 被轉化成一系列的fragment 或者稱為畫素 這些fragment接下來要做ps操作,此時每個fragment都有位置 顏色 紋理座標這些屬性資訊,這些屬性資訊通過...