GAMES101作业2

声明:使用的是vs2022版,以下内容如有问题,感谢各位大佬指正!

作业要求:

作业目的:在屏幕上画出一个实心的三角形(深度测试)

我们需要做的:
| 💡在main.cpp中修改
|
| 1. Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();//中粘贴自己第一次的代码
| 2. rasterize_triangle(): 执行三角形栅格化算法//写包围盒、判断采样点、透视矫正插值计算、深度测试、像素更新
| 3. static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。

关键词:包围盒;插值计算;深度测试;判断点是否在三角形内;光栅化;

1.粘贴自己第一次代码

cpp 复制代码
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,float zNear, float zFar)
{
    // Students will implement this function

    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();

    // TODO: Implement this function
    // Create the projection matrix for the given parameters.
    // Then return it.
    
    //粘贴第一次作业
    float n = zNear;
    float f = zFar;
    Eigen::Matrix4f M_persp2Ortho;
    M_persp2Ortho << n, 0, 0, 0,
        0, n, 0, 0,
        0, 0, n + f, -n * f,
        0, 0, 1, 0;

    float fov = eye_fov * MY_PI / 180.0;
    float t = -n * tan(fov / 2.);//解决原三角倒着的方法,将t换为-t
    float b = -t;
    float r = aspect_ratio * t;
    float l = -r;
    
    Eigen::Matrix4f M_ortho, trans, scale;
    trans << 1, 0, 0, -(r + l) / 2,
        0, 1, 0, -(t + b) / 2,
        0, 0, 1, -(n + f) / 2,
        0, 0, 0, 1;
    scale << 2 / (r - l), 0, 0, 0,
        0, 2 / (t - b), 0, 0,
        0, 0, 2 / (n - f), 0,
        0, 0, 0, 1;
    M_ortho = scale * trans;

    projection = M_ortho * M_persp2Ortho;
    return projection;
}

2.rasterize_triangle(): 执行三角形栅格化算法

分三步:

1.写三角形包围盒,计算三角形在屏幕空间的边界值

cpp 复制代码
void rst::rasterizer::rasterize_triangle(const Triangle& t)
{
    auto v = t.toVector4();//v 是三角形三个顶点的齐次坐标(x,y,z,w)
    
    // TODO : Find out the bounding box of current triangle.
    
    //包围盒(计算三角形在屏幕空间中的边界值)AABB
    float min_x = width;
    float max_x = 0;
    float min_y = height;
    float max_y = 0;

    for (int i = 0; i < 3; i++)
    {
        min_x = std::min(v[i].x(), min_x);
        max_x = std::max(v[i].x(), max_x);
        min_y = std::min(v[i].y(), min_y);
        max_y = std::max(v[i].y(), max_y);
    }

2.判断采样点是否在三角形内,计算采样点的重心坐标,然后透视矫正插值计算深度和颜色

https://zhuanlan.zhihu.com/p/403259571#:~:text=这里我们采用线性插值,可以得到P'%3DA'%2Bm (B'-A'),如果设|A'B'|为1,则|A'P'|为m,|P'B'|为1-m。 现在我们的问题是:已知屏幕上一点P',且 P'%3D (1-m)A'%2BmB' ,还有顶点A和B的世界坐标,,需要求出与P'对应的点P关于AB的表示: P%3D (1-n)A%2BnB 。 公式推导 这里我添加两条辅助线,方便公式推导:从A和B作竖直线,分别交P'P延长线上的G和K。//来自知乎文章---图形学 - 关于透视矫正插值那些事

cpp 复制代码
    // iterate through the pixel and find if the current pixel is inside the triangle
    //遍历像素,找出当前像素是否在三角形内
    // If so, use the following code to get the interpolated z value.
    //如果是这样,请使用以下代码获取插值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;
    
    for (int y = min_y; y < max_y; y++)
    {
        for (int x = min_x; x < max_x; x++)
        {
            //遍历边界框内的所有像素,检查中心点是否在三角形内
            if (insideTriangle(x + 0.5, y + 0.5, t.v))//使用 (x + 0.5, y + 0.5) 作为像素中心点
            {
                //透视矫正插值计算深度和颜色
                auto [alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 0.5, t.v);//计算该点的重心坐标
                // 计算:1/(α + β + γ)
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                // 计算:α(z₀/w₀) + β(z₁/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();
                // 相乘,得到最终插值结果
                z_interpolated *= w_reciprocal;

                

3.深度测试与像素更新

cpp 复制代码
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.

                int index = get_index(x, y);
                if (z_interpolated < depth_buf[index]) 
                {
                    Eigen::Vector3f p;
                    p << x, y, z_interpolated;
                    set_pixel(p, t.getColor());
                    depth_buf[index] = z_interpolated;
                }
            }
        }
    }
}

2.rasterize_triangle():的完整代码

cpp 复制代码
void rst::rasterizer::rasterize_triangle(const Triangle& t)
{
    auto v = t.toVector4();//v 是三角形三个顶点的齐次坐标(x,y,z,w)
    
    // TODO : Find out the bounding box of current triangle.
    
    //包围盒(计算三角形在屏幕空间中的边界值)AABB
    float min_x = width;
    float max_x = 0;
    float min_y = height;
    float max_y = 0;

    for (int i = 0; i < 3; i++)
    {
        min_x = std::min(v[i].x(), min_x);
        max_x = std::max(v[i].x(), max_x);
        min_y = std::min(v[i].y(), min_y);
        max_y = std::max(v[i].y(), max_y);
    }

    // iterate through the pixel and find if the current pixel is inside the triangle
    // If so, use the following code to get the interpolated z value.
    //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;
    
    for (int y = min_y; y < max_y; y++)
    {
        for (int x = min_x; x < max_x; x++)
        {
            //遍历边界框内的所有像素,检查中心点是否在三角形内
            if (insideTriangle(x + 0.5, y + 0.5, t.v))//使用 (x + 0.5, y + 0.5) 作为像素中心点
            {
                //透视矫正插值计算深度和颜色
                auto [alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 0.5, t.v);//计算该点的重心坐标
                // 计算:1/(α + β + γ)
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                // 计算:α(z₀/w₀) + β(z₁/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();
                // 相乘,得到最终插值结果
                z_interpolated *= w_reciprocal;

                // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
                // 深度测试与像素更新
                int index = get_index(x, y);//将坐标转化为索引
                if (z_interpolated < depth_buf[index]) //将当前像素的深度值和缓冲区中的进行比较
                {
                    //z 值越小越近
                    //如果当前像素更近,就更新颜色和深度缓冲区
                    Eigen::Vector3f p;
                    p << x, y, z_interpolated;
                    //更新帧缓冲区的颜色
                    set_pixel(p, t.getColor());
                    //更新深度缓冲区的值
                    depth_buf[index] = z_interpolated;
                }
            }
        }
    }
}

3.测试点是否在三角形内(叉积)Cross product

cpp 复制代码
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]

    int flag = -1;

    for (int i = 0; i < 3; i++)
    {
        Eigen::Vector3f p0 = { x,y,0 };       //待检测点
        Eigen::Vector3f p1 = _v[i];           //当前边的起点
        Eigen::Vector3f p2 = _v[(i + 1) % 3]; //当前边的终点

        Eigen::Vector3f v1 = p1 - p0;         //从待检测点到边起点的向量
        Eigen::Vector3f v2 = p1 - p2;         //边的方向向量

        float cp = v1.cross(v2).z();          //计算叉积的z分量
        if (cp == 0) continue;                // 点在边上,继续检查下一条边

        int sign = cp < 0 ? 0 : 1;            // 获取叉积的符号(0为负,1为正)
        if (flag == -1) flag = sign;          // 记录第一个非零叉积的符号
        if (flag != sign)return false;        // 符号不一致,点在三角形外部
    }

    return true;                              // 所有边的叉积符号一致,点在三角形内部
    
 }

最后运行,得到这幅图


相关推荐
擎天柱工坊5 小时前
干货 | RK3588硬件全开源设计笔记1-芯片PCB叠层阻抗设计
学习笔记·rk3588·pcb设计·芯片特性·叠层设计·阻抗设计
常驻客栈3 天前
B-深入理解计算机系统第3版之第11章:网络编程
开发语言·笔记·学习笔记·常驻客栈·深入理解计算机系统
常驻客栈3 天前
B-深入理解计算机系统第3版之第10章:系统级 I/O
开发语言·笔记·学习笔记·常驻客栈·深入理解计算机系统
SomeBottle10 天前
【小记】上手 Pi,记录一下我的 AI 编码实践
学习笔记·ai agent
hsjiasb10 天前
FreeRTOS学习(三十九)——最终总结
学习·学习笔记·freertos
常驻客栈10 天前
B-深入理解计算机系统第3版之第2章:信息的表示和处理
笔记·学习笔记·常驻客栈·深入理解计算机系统·经典书籍
hsjiasb10 天前
FreeRTOS学习(三十八)——性能优化
学习·学习笔记·freertos
hsjiasb11 天前
FreeRTOS学习(三十七)——常见错误与工程规范
学习·学习笔记·freertos
金字塔頂の蝸牛11 天前
商务日语口语实用手册
笔记·学习笔记·日语·外语
hsjiasb12 天前
FreeRTOS学习(三十五)——系统架构综合项目
学习·学习笔记·freertos