声明:使用的是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.判断采样点是否在三角形内,计算采样点的重心坐标,然后透视矫正插值计算深度和颜色
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; // 所有边的叉积符号一致,点在三角形内部
}
最后运行,得到这幅图