计算机图形学games101作业2

591 阅读2分钟

题目描述:在屏幕上画出一个实心三角形, 换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数 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(): 测试点是否在三角形内。你可以修改此函 数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。

总体思路:

通过判断视口中的坐标点是否在三角形内找到需要着色的点,由于需要着色的点的深度有大有小(通过插值计算),着色时可能会被覆盖,所以需要通过深度信息判断,深度值越大离视点越远,这个过程中需要更新深度信息,当前点深度值小会将之前点着色覆盖。

代码如下:

//光栅化三角形
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    //在视口屏幕上遍历所有的点判断是否在三角形内部,在内部就需要更新深度缓存和帧缓存
    for(int x=0;x<width;x++){
        for(int y=0;y<height;y++){
            //判断一点是否在三角形内部,使用的是每一个像素的中心位置进行判断
            if(insideTriangle(x+0.5,y+0.5,t.v)){
                //该部分代码实现对每个点的z插值计算
                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(Eigen::Vector3f{x,y,z_interpolated},t.getColor());
                }
            }
        }
    }
}

判断屏幕上的点是否在三角形中

//计算向量交叉乘积
inline float crossProduct(float x1,float y1,float x2,float y2){
    return x1*y2-x2*y1;
}
//通过判断向量的交叉乘积判断点是否在三角形内部
static bool insideTriangle(int x, int y, const Vector3f* _v)
{   
    float x1=_v[0].x(),y1=_v[0].y(),x2=_v[1].x(),y2=_v[1].y(),x3=_v[2].x(),y3=_v[2].y();
    if(crossProduct(x3-x1,y3-y1,x2-x1,y2-y1)>=0){
        float tmpx = x2;
        float tmpy = y2;
        x2 = x3;
        y2 = y3;
        x3 = tmpx;
        y3 = tmpy;
    }
    if(crossProduct(x2 - x1, y2 - y1, x - x1, y - y1) < 0) return false;
    if(crossProduct(x3 - x2, y3 - y2, x - x2, y - y2) < 0) return false;
    if(crossProduct(x1 - x3, y1 - y3, x - x3, y - y3) < 0) return false;
    return true;
}