games101作业2(黑边问题暂未解决)

作业内容

•  [5 ] 正确地提交所有必须的文件,且代码能够编译运行。

•  [20 ] 正确实现三角形栅格化算法。

•  [10 ] 正确测试点是否在三角形内。

  [10 ] 正确实现 z-buffer 算法, 将三角形按顺序画在屏幕上。

  [提高项 5 ]  super-sampling 处理 Anti-aliasing : 你可能会注意  到,当我们放大图像时,图像边缘会有锯齿感。我们可以用 super-sampling 来解决这个问题,即对每个像素进行 2 * 2 采样,并比较前后的结果 (这里 并不需要考虑像素与像素间的样本复用)。需要注意的点有, 对于像素内的每  一个样本都需要维护它自己的深度值,即每一个像素都需要维护一个 sample list。最后, 如果你实现正确的话,你得到的三角形不应该有不正常的黑边。

insideTriangle

// 检查点 (x, y) 是否位于三角形内部,使用向量叉积计算点与三角形三条边的方向关系
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]
    Eigen::Vector3f p(x, y, 1.0f);

    Eigen::Vector3f edge1 = _v[1] - _v[0];
    Eigen::Vector3f edge2 = _v[2] - _v[1];
    Eigen::Vector3f edge3 = _v[0] - _v[2];

    Eigen::Vector3f c1 = p - _v[0];
    Eigen::Vector3f c2 = p - _v[1];
    Eigen::Vector3f c3 = p - _v[2];

    float z1 = edge1.cross(c1).z();
    float z2 = edge2.cross(c2).z();
    float z3 = edge3.cross(c3).z();

    // 如果点的方向一致,则在三角形内部
    return (z1 > 0 && z2 > 0 && z3 > 0) || (z1 < 0 && z2 < 0 && z3 < 0);

}

rasterize_triangle

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    
    // 将三角形顶点转换为 4D 向量(裁剪空间坐标)
    auto v = t.toVector4();

    // Step 1: 计算三角形的包围盒
    // 找到三角形顶点的最小和最大 x/y 值,构建包围盒
    // 包围盒范围缩小了像素迭代的区域,提高光栅化效率
    float minX = std::floor(std::min({v[0].x(), v[1].x(), v[2].x()}));
    float maxX = std::ceil(std::max({v[0].x(), v[1].x(), v[2].x()}));
    float minY = std::floor(std::min({v[0].y(), v[1].y(), v[2].y()}));
    float maxY = std::ceil(std::max({v[0].y(), v[1].y(), v[2].y()}));

    // Step 2: 遍历包围盒内的每个像素
    // 使用整数循环确保效率,逐像素检查是否位于三角形内
    for (int x = minX; x <= maxX; x++) {
        for (int y = minY; y <= maxY; y++) {

            // 使用像素中心点 (x+0.5, y+0.5) 判断是否在三角形内
            if (insideTriangle(x + 0.5f, y + 0.5f, t.v)) {

                // Step 3: 计算像素的重心坐标 (alpha, beta, gamma)
                // 通过重心插值计算深度值和颜色值
                auto [alpha, beta, gamma] = computeBarycentric2D(x + 0.5f, y + 0.5f, t.v);

                // Step 4: 插值深度值 z
                // w_reciprocal: 计算 w 的倒数,用于归一化重心坐标
                float w_reciprocal = 1.0f / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());

                // 插值的 z 值,按权重计算,结合 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()) * w_reciprocal;

                // Step 5: 深度测试
                // 确保当前像素的 z 值比深度缓冲区中的值小
                int index = get_index(x, y); // 获取像素对应的深度缓冲区索引
                if (z_interpolated < depth_buf[index]) {
                    // 更新深度缓冲区的 z 值
                    depth_buf[index] = z_interpolated;

                    // Step 6: 绘制像素
                    // 构造像素位置和颜色
                    Vector3f point = Vector3f((float)x, (float)y, z_interpolated); // 像素位置
                    Vector3f color = t.getColor(); // 从三角形对象获取颜色
                    set_pixel(point, color); // 设置像素到帧缓冲区
                }
            }
        }
    }
}

解决三角形颠倒问题

将main.cpp文件中

 r.set_projection(get_projection_matrix(45, 1, 0.1, 50));

改为

 r.set_projection(get_projection_matrix(45, 1, -0.1, -50));

运行结果

放大图像后可以发现三角形边界有明显的锯齿。

附加题:MSAA  Anti-aliasing

rasterizer.cpp

// 定义 MSAA 相关常量(8x MSAA)
// msaa_samples:每个像素的子采样点数量
// sample_weight:每个子采样点的权重
// sample_offsets:用于 MSAA 的子采样点偏移量(分布在 8 个不同位置)
constexpr int msaa_samples = 8; // 8x MSAA
constexpr float sample_weight = 1.0f / msaa_samples; // 每个子采样点的权重
constexpr std::array<std::pair<float, float>, msaa_samples> sample_offsets = {{
    {0.125f, 0.125f}, {0.375f, 0.125f}, {0.625f, 0.125f}, {0.875f, 0.125f},
    {0.125f, 0.875f}, {0.375f, 0.875f}, {0.625f, 0.875f}, {0.875f, 0.875f}
}};
//光栅化三角形
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    

    auto v = t.toVector4();

    // Step 1: 获取三角形的包围盒
    float minX = std::floor(std::min({v[0].x(), v[1].x(), v[2].x()}));
    float maxX = std::ceil(std::max({v[0].x(), v[1].x(), v[2].x()}));
    float minY = std::floor(std::min({v[0].y(), v[1].y(), v[2].y()}));
    float maxY = std::ceil(std::max({v[0].y(), v[1].y(), v[2].y()}));

    // Step 2: 遍历包围盒中的每个像素
    for (int x = minX; x <= maxX; x++) {
        for (int y = minY; y <= maxY; y++) {
            Vector3f final_color = Vector3f::Zero(); // 累积颜色
            int covered_samples = 0; // 记录落在三角形内的采样点数

            // Step 3: 遍历每个子采样点
            for(size_t i = 0; i < sample_offsets.size(); ++i){
                float dx = sample_offsets[i].first;     // 获取第 i 个偏移点的 x 坐标
                float dy = sample_offsets[i].second;    // 获取第 i 个偏移点的 y 坐标
                float sx = x + dx;  // 子采样点的实际坐标x
                float sy = y + dy;  //子采样点的实际坐标y

                // 子采样点的深度缓冲区索引
                int depth_index = get_index(x, y) * msaa_samples + i; 

                if (insideTriangle(sx,sy,t.v)) {
                    // Step 4: 计算采样点的重心坐标
                    auto [alpha, beta, gamma] = computeBarycentric2D(sx, sy, t.v);

                    // 插值深度值
                    float w_reciprocal = 1.0f / (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[depth_index]) {            
                        depth_buf[depth_index] = z_interpolated;
                        final_color += t.getColor();  // 累加采样点颜色
                        covered_samples++;
                    }

                }
            }

            // Step 5: 平均采样点颜色,设置像素颜色
            if (covered_samples > 0) {
                final_color /= covered_samples;     // 平均采样点颜色
                final_color = final_color * (covered_samples / (float)msaa_samples) + 
                                frame_buf[get_index(x, y)] * (1.0f - covered_samples / (float)msaa_samples); // 背景混合
                Vector3f pixel_position = Vector3f(x, y, 0);    // 像素位置
                set_pixel(pixel_position, final_color);     // 设置像素颜色
            }
        }
    }

}
// 光栅化器构造函数
// 初始化帧缓冲区和深度缓冲区的大小
rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    // 初始化帧缓冲区
    frame_buf.resize(w * h); 
    // 初始化 MSAA 深度缓冲区
    depth_buf.resize(w * h * msaa_samples, std::numeric_limits<float>::infinity()); 
}

运行结果

三角形边缘相对顺滑,但是三角形交界处存在重叠情况,形成不正常的“黑边”。

黑边问题目前还未解决...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值