题目: 需要填写的函数有以下两个:
- rasterize_triangle(): 执行三角形栅格化算法
在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是 那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形, 换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数 rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。
该函数的内部工作流程如下:
- 创建三角形的 2 维 bounding box。
- 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中
心的屏幕空间坐标来检查中心点是否在三角形内。 - 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。 - 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
- static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函
数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
测试点是否在三角形内的函数:
static bool insideTriangle(double x, double y, const Vector3f* _v)
{
Eigen::Vector2f p;
p << x, y;
Eigen::Vector2f AB = _v[1].head(2) - _v[0].head(2);
Eigen::Vector2f BC = _v[2].head(2) - _v[1].head(2);
Eigen::Vector2f CA = _v[0].head(2) - _v[2].head(2);
Eigen::Vector2f AP = p - _v[0].head(2);
Eigen::Vector2f BP = p - _v[1].head(2);
Eigen::Vector2f CP = p - _v[2].head(2);
return AB[0] * AP[1] - AB[1] * AP[0] > 0
&& BC[0] * BP[1] - BC[1] * BP[0] > 0
&& CA[0] * CP[1] - CA[1] * CP[0] > 0;
}
光栅化函数:
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
float min_x = std::min(v[0][0], std::min(v[1][0], v[2][0]));
float max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));
float min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));
float max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));
min_x = static_cast<int>(std::floor(min_x));
min_y = static_cast<int>(std::floor(min_y));
max_x = static_cast<int>(std::ceil(max_x));
max_y = static_cast<int>(std::ceil(max_y));
bool MSAA = true;
if (MSAA)
{
std::vector<Eigen::Vector2f> pos
{
{0.25,0.25},
{0.75,0.25},
{0.25,0.75},
{0.75,0.75}
};
for (int i = min_x; i <= max_x; ++i)
{
for (int j = min_y; j <= max_y; ++j)
{
int count = 0;
float minDepth = FLT_MAX;
for (int MSAA_4 = 0; MSAA_4 < 4; ++MSAA_4)
{
if (insideTriangle(static_cast<float>(i+pos[MSAA_4][0]), static_cast<float>(j+pos[MSAA_4][1]),t.v))
{
auto[alpha, beta, gamma] = computeBarycentric2D(static_cast<float>(i + pos[MSAA_4][0]), static_cast<float>(j + pos[MSAA_4][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;
minDepth = std::min(minDepth, z_interpolated);
++count;
}
}
if (count)
{
if (depth_buf[get_index(i, j)] > minDepth)
{
depth_buf[get_index(i, j)] = minDepth;
Eigen::Vector3f color = t.getColor() * (count / 4.0);
Eigen::Vector3f point;
point << static_cast<float>(i), static_cast<float>(j), minDepth;
set_pixel(point, color);
}
}
}
}
}
else
{
for (int i = min_x; i <= max_x; ++i)
{
for (int j = min_y; j <= max_y; ++j)
{
if (insideTriangle(static_cast<float>(i+0.5), static_cast<float>(j+0.5),t.v))
{
auto [alpha, beta, gamma] = computeBarycentric2D(static_cast<float>(i + 0.5), static_cast<float>(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;
if (depth_buf[get_index(i, j)] > z_interpolated)
{
depth_buf[get_index(i, j)] = z_interpolated;
Eigen::Vector3f color = t.getColor();
Eigen::Vector3f point;
point << static_cast<float>(i), static_cast<float>(j), z_interpolated;
set_pixel(point, color);
}
}
}
}
}
}
在一开始的时候三角形是黄色三角形在后,蓝色三角形在前。并且两个三角形都是朝下方向,在上网查询后发现可能出错的原因是:
- opencv左上为原点,课程中左下为原点,所以导致y,z两轴是反着的。
最后的效果: 这是最后的效果,本来两个三角形的顺序是相反的,发现pdf要求的不一样,又上网查阅,更改了get_projection_matrix()函数的zNear和zFar,让这两个参数在形参列表的位置互换。
但又在我想更改上下方向的时候,也就是更改了get_projection_matrix()函数中的
- zNear/zFar *=-1;
- float top = – tan(halve)*zNear;
发现出现了下图的效果: 目前这个bug还未修正,等以后学习更多再回来思考更改。
|