从零开始写光栅化渲染器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);
    }
}

效果图:
这里写图片描述

项目完整地址:

3DRender: https://github.com/zhanghuanzj/3DRender.git

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值