Assignment2
前置知识
光栅化
将数据绘制成图像(呈现到屏幕上)
采样:用某种方法(函数)对某个点给出确定的值
光栅化采样的任务:屏幕上的像素是离散的,要判断该像素是否需要绘制(该点是否在绘制的三角形中)
包围盒(Bounding Box):减少需要扫描的范围,取三角形三个点XY的最大和最小值,构建一个正方形的包围盒
走样
走样本质:采样的频率低于信号频率,使信号与信号之间发生重叠
反走样(antialiasing):先模糊,再采样;本质是滤波使信号“变窄了”,截去了重叠的部分
MSAA(Supersampling):将一个像素分成多个小像素,每个像素独立计算是否在三角形内部,最后根据结果在大像素内取平均
code
static bool insideTriangle(int x, int 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 OA = Vector3f(_v[0].x() - x, _v[0].y() - y,0);
Eigen::Vector3f OB = Vector3f(_v[1].x() - x, _v[1].y() - y,0);
Eigen::Vector3f OC = Vector3f(_v[2].x() - x, _v[2].y() - y,0);
// 若三条边两两叉积方向一致,则在三角形内
auto a = OA.cross(OB);
auto b = OB.cross(OC);
auto c = OC.cross(OA);
return (a.dot(b) > 0 && b.dot(c) > 0);
}
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// TODO : Find out the bounding box of current triangle.
// iterate through the pixel and find if the current pixel is inside the triangle
// 取三角形三个点XY的最大和最小值,构建一个正方形的包围盒
float BoxRight = std::max({t.v[0].x(), t.v[1].x(), t.v[2].x()});
float BoxLeft = std::min({t.v[0].x(), t.v[1].x(), t.v[2].x()});
float BoxBottom = std::max({t.v[0].y(), t.v[1].y(), t.v[2].y()});
float BoxTop = std::min({t.v[0].y(), t.v[1].y(), t.v[2].y()});
// 打包成lambda,方便实现MSAA
auto fun = [&](float x, float y, int& count) -> float{
x = (int)x + 0.5f;
y = (int)y + 0.5f;
// 比较深度值
// 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;
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
float& z_depth = rasterizer::depth_buf[get_index(x, y)];
if (z_interpolated < z_depth)
{
//z_depth = z_interpolated;
//set_pixel(Vector3f(x, y, 0), t.getColor());
++count;
return z_interpolated;
}
else
return z_depth;
};
// 遍历包围盒中像素,判断像素是否在三角形内部
for (int x = BoxLeft; x < BoxRight; ++x)
for (int y = BoxTop; y < BoxBottom; ++y)
{
int count = 0;
float z = 0;
if (insideTriangle(x + 0.25f, y + 0.25f, t.v))
z = std::max(0.0f, fun(x + 0.25f, y + 0.25f, count));
if (insideTriangle(x + 0.75f, y + 0.25f, t.v))
z = std::max(0.0f, fun(x + 0.75f, y + 0.25f, count));
if (insideTriangle(x + 0.25f, y + 0.75f, t.v))
z = std::max(0.0f, fun(x + 0.25f, y + 0.75f, count));
if (insideTriangle(x + 0.75f, y + 0.75f, t.v))
z = std::max(0.0f, fun(x + 0.75f, y + 0.75f, count));
if (count)
{
set_pixel(Vector3f(x, y, 0), t.getColor() * count / 4.0f);
if (z)
rasterizer::depth_buf[get_index(x, y)] = z;
}
}
}
注意事项
判断点是否在三角形中
将三角形三个点分别命名为ABC,设点O,连接OA、OB、OC,判断OA和OB、OB和OC、OC和OA两两叉乘方向是否一致,若一致,则点在三角形内
实现MSAA时,同一像素中每个样本点如何共用同一个深度值
如果每个样本点都需维护自己的深度值的话,整个depth buffer就是原来4倍,空间消耗大。于是我先将x和y取整成x+0.5、y+0.5f(和原来一样),如果样本点中至少有一个点需要绘制(在三角形中且离相机更近),则将该样本点的深度值作为整个点的深度值