我们已经知道如何绘制一条2D直线,接下来我们开始绘制实心三角形。其实原理比较简单,如下图是一个平底三角形,V2.y==V3.y,所以我们只要沿着三角形的两条边不断画直线,就可以将三角形填满。首先我们必须确定每条直线的左右两个点,即 VL和VR ,我们可以通过插值法确定,例如 VL.x 可以这样得到:
VL.x=V1.x+(V2.x−V1.x)∗factor
factor=(VL.y−V1.y)/(V2.y−V1.y)
所以
VL=V1.interp(V2,factor)
VR=V1.interp(V3,factor)
当得到 VL和VR 后,我们便可以绘制水平直线,每一个直线上的点V,其y值相同,点V同样可以通过插值进行计算得到。从而构成点到线,线到面的绘制。同理,平顶三角形也可以如此绘制。
一般三角形如下图所示,我们可以将其划分为一个平底三角形( △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(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(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.position.x)
{
swap(v4,v2);
}
draw_button_flat_triangle(v1,v2,v4);
draw_top_flat_triangle(v2,v4,v3);
}
}
项目完整地址:
3DRender: https://github.com/zhanghuanzj/3DRender.git