1. 程式人生 > >從零開始寫光柵化渲染器3:三角形光柵化

從零開始寫光柵化渲染器3:三角形光柵化

1.平底三角形光柵化

我們已經知道如何繪製一條2D直線,接下來我們開始繪製實心三角形。其實原理比較簡單,如下圖是一個平底三角形,V2.y==V3.y,所以我們只要沿著三角形的兩條邊不斷畫直線,就可以將三角形填滿。首先我們必須確定每條直線的左右兩個點,即VLVR,我們可以通過插值法確定,例如VL.x可以這樣得到:

  VL.x=V1.x+(V2.xV1.x)factor

  factor=(VL.yV1.y)/(V2.yV1.y)
所以
  VL=V1.interp(V2,factor)
  VR=V1.interp(V3,factor)
這裡寫圖片描述
當得到VLVR後,我們便可以繪製水平直線,每一個直線上的點V,其y值相同,點V同樣可以通過插值進行計算得到。從而構成點到線,線到面的繪製。同理,平頂三角形也可以如此繪製。

2.一般三角形的繪製

一般三角形如下圖所示,我們可以將其劃分為一個平底三角形(V1V2V4)和一個平頂三角形(V2V3V4),再分別進行繪製。

這裡寫圖片描述
程式碼如下:

struct Scanline
{
    Scanline(const Vertex &v_,const Vertex &Step_,int x_,int y_,float width_):v(v_),step(Step_),x(x_),y(y_),width(width_){}
    Vertex v;
    Vertex step;
    int x;
    int y;
    float width;
}; void sort_vertex(Vertex &v1,Vertex &v2,Vertex &v3) { if (v1.position.y>v2.position.y || (v1.position.y==v2.position.y&&v1.position.x>v2.position.x) ) { swap(v1,v2); } if (v2.position.y>v3.position.y || (v2.position.y==v3.position.y&&v2.position
.x>v3.position.x) ) { swap(v2,v3); } if (v1.position.y>v2.position.y || (v1.position.y==v2.position.y&&v1.position.x>v2.position.x) ) { swap(v1,v2); } } Scanline generate_scanline(Vertex vl,Vertex vr) { float width = vr.position.x - vl.position.x; int startX = vl.position.x+0.5; Vertex step((vr.position-vl.position)/width,(vr.normal-vl.normal)/width,(vr.color-vl.color)/width,(vr.u-vl.u)/width,(vr.v-vl.v)/width); return Scanline(vl,step,startX,vl.position.y+0.5,width); } void draw_scanline(Vertex vl,Vertex vr) { Scanline scanline = generate_scanline(vl,vr); int lenght = scanline.width+0.5; for (int i=0;i<=lenght;++i) { DirectX::instance().drawPixel(scanline.x+i,scanline.y,scanline.v.color); scanline.v.add(scanline.step); } } void draw_top_flat_triangle(Vertex v1,Vertex v2,Vertex v3) { int startY = v1.position.y + 0.5; int endY = v3.position.y + 0.5; for (int y=startY;y<=endY;++y) { float factor = static_cast<float>(y-startY)/(endY-startY); Vertex vl = v1.interp(v3,factor); Vertex vr = v2.interp(v3,factor); draw_scanline(vl,vr); } } void draw_button_flat_triangle(Vertex v1,Vertex v2,Vertex v3) { int startY = v1.position.y + 0.5; int endY = v3.position.y + 0.5; for (int y=startY;y<=endY;++y) { float factor = static_cast<float>(y-startY)/(endY-startY); Vertex vl = v1.interp(v2,factor); Vertex vr = v1.interp(v3,factor); draw_scanline(vl,vr); } } void draw_triangle(Vertex v1,Vertex v2,Vertex v3) { sort_vertex(v1,v2,v3); if (v1.position.y==v2.position.y) { draw_top_flat_triangle(v1,v2,v3); } else if (v2.position.y==v3.position.y) { draw_button_flat_triangle(v1,v2,v3); } else { float factor = (v2.position.y-v1.position.y)/(v3.position.y-v1.position.y); Vertex v4 = v1.interp(v3,factor); if (v4.position.x<v2.position.x) { swap(v4,v2); } draw_button_flat_triangle(v1,v2,v4); draw_top_flat_triangle(v2,v4,v3); } }

效果圖:
這裡寫圖片描述

專案完整地址: