Games 101 作业2 超详细说明

首先是main函数中的矩阵变换函数,这个部分在作业1已经实现过了,可以直接进行复制。

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
  
    // TODO: Copy-paste your implementation from the previous assignment.
    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
    Matrix4f pro(4, 4);
    //透视变换
    pro << zNear, 0, 0, 0,
        0, zNear, 0, 0,
        0, 0, zNear + zFar, -zNear * zFar,
        0, 0, 1, 0;
    Matrix4f orth(4, 4);

    float t = tanf(eye_fov / 2) * zNear * 2;
    float width = aspect_ratio * t;

    //平移矩阵
    Matrix4f move(4, 4);
    move << 1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, -(zFar + zNear) / 2,
        0, 0, 0, 1;
    //缩放矩阵
    orth <<
        2 / width, 0, 0, 0,
        0, 2 / t, 0, 0,
        0, 0, 2 / (zFar - zNear), 0,
        0, 0, 0, 1;

    //注意乘积的顺序
    projection = orth * move * pro;

    return projection;
}

光栅化部分:

①检测当前点是否在所给三角形内

static bool insideTriangle(float x, float y, const Vector3f* _v)
{
    // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]

    Vector3f ans1, ans2, ans3, vec1, vec2, vec3;

    vec1 = _v[1] - _v[0];
    ans1 = vec1.cross(Vector3f(x - _v[0][0], y - _v[0][1], 0));

    vec2 = _v[2] - _v[1];
    ans2 = vec2.cross(Vector3f(x - _v[1][0], y - _v[1][1], 0));

    if (ans1.transpose() * ans2 < 0)
        return false;

    vec3 = _v[0] - _v[2];
    ans3 = vec3.cross(Vector3f(x - _v[2][0], y - _v[2][1], 0));
    if (ans1.transpose() * ans3 < 0)
        return false;

    return true;

}

思路:参照课程ppt,利用向量叉乘的性质对其进行判断。

若 “三角形的三个边对应的向量” 分别叉乘 “顶点到目标点所成向量” 所得向量均为同方向,则说明该点在所给三角形内部;反之,则说明点在三角形外部。

(本质就是“同侧 异侧”的问题,两向量交换位置,使用右手定则可以判断两次叉乘所得向量的方向相反,那么所得结果再相乘就会是负数

②采样部分

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();

    //获取三角形的边界(外接矩形)
    int x_min = min(v[0].x(), max(v[1].x(), v[2].x()));
    int x_max = max(v[0].x(), max(v[1].x(), v[2].x()));

    int y_min = min(v[0].y(), min(v[1].y(), v[2].y()));
    int y_max = max(v[0].y(), min(v[1].y(), v[2].y()));

    /*int width = x_max - x_min + 1;
    int height = y_max - y_min + 1;*/




    // TODO : Find out the bounding box of current triangle.
    // iterate through the pixel and find if the current pixel is inside the triangle
    for (int i = x_min; i <= x_max; ++i)
        for (int j = y_min; j < y_max; ++j) {
           
            //如果该点在三角形内,则进行进一步判断
            if (insideTriangle(i+0.5, j+0.5, t.v)) {
               auto [alpha, beta, gamma] = computeBarycentric2D(i+0.5, j+0.5, 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;

                //调用已经写好的获取数组对应下标位置函数
                int cur_index = get_index(i, j);
                
                //如果插值所得的该点深度小于缓存的深度,则进行替换(表示该点更近!)
                if (z_interpolated < depth_buf[cur_index]) {
                    depth_buf[cur_index] = z_interpolated;
                    
                    //这个point只是为了调用函数set_pixel而创建,只用到了x和y(用来获取该点在颜色储存数组中的对应位置并进一步更新),其z值实际上并没有任何作用,可以任意设值
                    Vector3f point;
                    point << i, j, z_interpolated;
                    /*frame_buf[cur_index] = t.getColor();*/
                    //设置对应的颜色
                    set_pixel(point, t.getColor());
                }

            }
        }



    
}

几个坑点:

insideTriangle(x, y, t.v) 函数中,实际检测点的坐标应该是(x+0.5,y+0.5)

上课有提到坐标对应的是左下角点,检测应输入中心点坐标! 

computeBarycentric2D(x, y, t.v) 也是如此!

ps:有些朋友可能不知道computeBarycentric2D(x, y, t.v) 有什莫用,其实就是为了获得该点的z值而进行的相关计算,所以也应该填入当前点对应的像素中心坐标!

附加题:

使用MASS:

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    //计算bounding box 边界(外接矩形)
    int x_min = min(v[0].x(), max(v[1].x(), v[2].x()));
    int x_max = max(v[0].x(), max(v[1].x(), v[2].x()));

    int y_min = min(v[0].y(), min(v[1].y(), v[2].y()));
    int y_max = max(v[0].y(), min(v[1].y(), v[2].y()));

    /*int width = x_max - x_min + 1;
    int height = y_max - y_min + 1;*/

    //我们一个正方体继续划分成了四个小正方体
    // pos数组记录的是相对于左下角点的坐标偏移量
    float pos[][2] = { {0.25,0.25},{0.25,0.75}, {0.75,0.25}, {0.75, 0.75} };
    for (int i = x_min; i <= x_max; ++i)
        for (int j = y_min; j < y_max; ++j) {
            float count = 0; //计数器 用于记录4个子采样点在三角形中的个数
            float min_distance = FLT_MAX;//用于记录四个子采样点中最小的z值
            for (int l = 0; l < 4; ++l) {
                if (insideTriangle((float)i + pos[l][0],(float) j + pos[l][1], t.v)) {
                    count+=1;

                    auto [alpha, beta, gamma] = computeBarycentric2D(i + pos[l][0], j + pos[l][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;

                    //更新最小的z值
                    min_distance = min(z_interpolated, min_distance);
                }
            }

            //如果四个子采样点中至少有一个在三角形内
            if (count > 0) {
                int cur_index = get_index(i, j);
                
                //且该点的最小z坐标比depth_buf中的还要小(更近)
                //则进行更新
                if (min_distance < depth_buf[cur_index]) {
                    depth_buf[cur_index] = min_distance;
                    Vector3f point;
                    point << i, j, 1;
                    /*frame_buf[cur_index] = t.getColor();*/

                    //这里我对set_pixel函数做了些更改,后文细说
                    set_pixel(point, t.getColor(),count);
               }
            }

           
        }
}

void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color,float count)
{
    //old index: auto ind = point.y() + point.x() * width;
    auto ind = (height-1-point.y())*width + point.x();
    //count 其实就相当于起到一个 ”比例“的作用!
    frame_buf[ind] = color *count /4.0;

}

注意点:

在MASS中,我们将一个小正方体切割成四个更小的正方体!

锚点坐标仍然是左下角的位置,使用了pos数组来储存四个点的相对位置偏移量!

count变量是用来记录当前四个子采样点中在三角形内的个数,后续set_pixel设置颜色的效果也会根据count的大小来调整相应的颜色比例。

注意注意注意!!!

insideTriangle(x,y,v.t) 需要进行调整,其接收变量类型由int设置为float!不然会自动取整没有效果!!! 

set_pixel()函数也需要修改,如上图代码所示!

效果对比:

原:

 使用MASS后:

 虽然边界抗锯齿好多了,但是在颜色重叠出出现了”黑点“,有些小问题。

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值