GAMES101作业(02)- Triangles and Z-buffering (实现光栅化和MSAA)

作业来自官网
相关课节知识点整理

问题描述

在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。
该函数的内部工作流程如下:

  1. 创建三角形的 2 维 bounding box。
  2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
  3. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。
  4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
    你需要修改的函数如下:
    • rasterize_triangle(): 执行三角形栅格化算法
    • static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。

原问题解答

1. 判断点是否在三角形内

知识点

对三组向量做叉积,如果得到的结果全为正或全为负,就说明点在三角形内。

static bool insideTriangle(float x, float y, const Vector3f* _v)
{
    
       
    
    bool flag[3];
    for (int i = 0; i < 3; i++)
    {
    
       
        float x1 = _v[(i+1)%3].x()- _v[i].x();
        float x2 = x-_v[i].x();
        float y1 = _v[(i+1)%3].y()- _v[i].y();
        float y2 = y-_v[i].y();
        flag[i]=x1*y2-x2*y1>0;
    }
    return flag[0]&&flag[1]&&flag[2]||!flag[0]&&!flag[1]&&!flag[2];
    
}

2. 光栅化三角形

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    
    
    auto v = t.toVector4();
    //将vec4转换成vec3,方便调用
    Vector3f v_[3];
    for(int i = 0;i<3;i++){
    
    
        v_[i]={
    
    v[i].x(),v[i].y(),v[i].z()};
    }
    //找到包围盒
    float min_x,max_x,max_y,min_y;
    min_x=std::min(std::min(v[0].x(),v[1].x()),v[2].x());
    min_y=std::min(std::min(v[0].y(),v[1].y()),v[2].y());
    max_x=std::max(std::max(v[0].x(),v[1].x()),v[2].x());
    max_y=std::max(std::max(v[0].y(),v[1].y()),v[2].y());
    //遍历包围盒内的所有像素
    for (int x = min_x; x < max_x; x++)
    {
    
    
        for (int y = min_y; y < max_y; y++)
        {
    
    
            if(insideTriangle(x+0.5f,y+0.5f,v_)){
    
    
                auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
                float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                //获得深度
                z_interpolated *= w_reciprocal;
                //如果新的深度小于存储的深度,则更新深度,然后重新设置该像素颜色
                if(z_interpolated<depth_buf[get_index(x,y)]){
    
    
                    depth_buf[get_index(x,y)]=z_interpolated;
                    set_pixel(Vector3f(x,y,z_interpolated),t.getColor());
                }
            }
        }    
    }
}

3. 运行效果

得到以下结果(应该是反了)
在这里插入图片描述

放大看看,可以看到在进行MSAA之前是有锯齿的。
在这里插入图片描述

扩展——进行MSAA进行锯齿处理

问题解答

rasterize_triangle与原来相同,然后:

  1. 因为原来的一个像素变成了2*2,所以要对深度存储的vector进行扩容。
  2. 然后,颜色也要存一下,要不然平均颜色没法求。
    rasterzier.hpp新增如下代码:
        std::vector<Eigen::Vector3f> color_list;

rasterzier.cpp改为:

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    
    
    frame_buf.resize(w * h);
    depth_buf.resize(w * h*4);
    color_list.resize(w*h*4);
}

rasterize_triangle:

//获得4个采样点
float dir[]={
    
    0.25,0.25,0.25,0.75,0.75,0.25,0.75,0.75};
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    
    
    auto v = t.toVector4();
    //将vec4转换成vec3,方便调用
    Vector3f v_[3];
    for(int i = 0;i<3;i++){
    
    
        v_[i]={
    
    v[i].x(),v[i].y(),v[i].z()};
    }
    float min_x,max_x,max_y,min_y;
    min_x=std::min(std::min(v[0].x(),v[1].x()),v[2].x());
    min_y=std::min(std::min(v[0].y(),v[1].y()),v[2].y());
    max_x=std::max(std::max(v[0].x(),v[1].x()),v[2].x());
    max_y=std::max(std::max(v[0].y(),v[1].y()),v[2].y());
    for (int x = min_x; x < max_x; x++)
    {
    
    
        for (int y = min_y; y < max_y; y++)
        {
    
    
            //划分4个采样点
            for(int i = 0;i<4;i++){
    
    
                if(insideTriangle((float)x+dir[i*2],(float)y+dir[i*2+1],v_)){
    
    
                    //获得深度
                    auto[alpha, beta, gamma] = computeBarycentric2D((float)x+dir[i*2],(float)y+dir[i*2+1], t.v);
                    float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                    float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                    z_interpolated *= w_reciprocal;
                    //更新该采样点的深度,同时修改该点记录的颜色并重新setpixel
                    if(z_interpolated<depth_buf[get_index(x,y)*4+i]){
    
    
                        depth_buf[get_index(x,y)*4+i]=z_interpolated;
                        color_list[get_index(x,y)*4+i]=t.getColor();
                        //重新计算该点像素的颜色,该像素颜色是所有取样点颜色的平均
                        Eigen::Vector3f new_color(0.0f,0.0f,0.0f);
                        for(int j=0;j<4;j++){
    
    
                            new_color+=color_list[get_index(x,y)*4+j];
                        }                        
                        new_color/=4;
                        set_pixel(Vector3f(x,y,z_interpolated),new_color);
                    }
                }
            }
        }
    }
}


运行效果

可以看到进行了抗锯齿。
eyi

心得

  1. mxn的矩阵只能和nxm相乘。。。。(居然被这个卡住了…)
  2. ./Rasterizer img.png保存图像,放大比较好看到抗锯齿效果,不要再列文虎克了。。。

猜你喜欢

转载自blog.csdn.net/weixin_43399489/article/details/120995939