光柵化插值方法

來源:互聯網
上載者:User

標籤:

光柵化是在電腦上產生映像的重要步驟,然而不管是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++) {for(int scrY=minY;scrY<=maxY;scrY++) {             ....}}

分別求取片段對應的標準化空間座標:

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) {float ndcX=0,ndcY=0,clipW=0;int scrAX,scrAY,scrBX,scrBY,scrCX,scrCY;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++) {for(int scrY=minY;scrY<=maxY;scrY++) {invViewPortTransform(scrX,scrY,fb->width,fb->height,ndcX,ndcY);VECTOR4D ndcPixel(ndcX,ndcY,1,0);VECTOR4D proportion4D=face->clipMatrixInv*ndcPixel;VECTOR3D proportionFragment(proportion4D.x,proportion4D.y,proportion4D.z);float pa=proportionFragment.x;float pb=proportionFragment.y;float pc=proportionFragment.z;float sum=pa+pb+pc;pa/=sum; pb/=sum; pc/=sum;if(pa<0||pb<0||pc<0)continue;Fragment frag;interpolate3f(pa,pb,pc,face->clipA.w,face->clipB.w,face->clipC.w,clipW);interpolate3f(pa,pb,pc,face->clipA.z,face->clipB.z,face->clipC.z,frag.ndcZ);frag.ndcZ/=clipW;if(frag.ndcZ<-1||frag.ndcZ>1)continue;if(db!=NULL) {float storeZ=readDepth(db,scrX,scrY);if(storeZ<frag.ndcZ)continue;writeDepth(db,scrX,scrY,frag.ndcZ);}interpolate3f(pa,pb,pc,face->clipA.x,face->clipB.x,face->clipC.x,frag.ndcX);frag.ndcX/=clipW;interpolate3f(pa,pb,pc,face->clipA.y,face->clipB.y,face->clipC.y,frag.ndcY);frag.ndcY/=clipW;interpolate3f(pa,pb,pc,face->clipA.nx,face->clipB.nx,face->clipC.nx,frag.nx);interpolate3f(pa,pb,pc,face->clipA.ny,face->clipB.ny,face->clipC.ny,frag.ny);interpolate3f(pa,pb,pc,face->clipA.nz,face->clipB.nz,face->clipC.nz,frag.nz);interpolate3f(pa,pb,pc,face->clipA.s,face->clipB.s,face->clipC.s,frag.s);interpolate3f(pa,pb,pc,face->clipA.t,face->clipB.t,face->clipC.t,frag.t);FragmentOut outFrag;fs(frag,outFrag);drawPixel(fb,scrX,scrY,outFrag.r,outFrag.g,outFrag.b);}}}

光柵化完成了,這下就能自己實現opengl和directx了!


著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

光柵化插值方法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.