作业2的整体代码框架和作业1相同,不同点在于这次需要绘制的是实心的三角形,且两个三角形之间有重叠,所以需要做深度检测。
这是此次作业的要求。
首先来实现三角形的栅格化算法函数 rasterize_triangle(const Triangle& t)
在给定的作业要求pdf中已经给了具体的算法,我们只需要将算法转换成代码即可。
1、首先需要建立三角形的bounding box,这个box就是一个二维的矩形,是可以把三角形包围起来的最小矩形
auto v = t.toVector4();//三角形的三个顶点,是一个包含三个Vector4f的array
Eigen::Vector2f minpoint, maxpoint;//创建的二维bounding box
minpoint.x() = MIN(MIN(v[0].x(), v[1].x()), v[2].x());
minpoint.y() = MIN(MIN(v[0].y(), v[1].y()), v[2].y());
maxpoint.x() = MAX(MAX(v[0].x(), v[1].x()), v[2].x());
maxpoint.y() = MAX(MAX(v[0].y(), v[1].y()), v[2].y());
这里我创建了两个vector来储存这个box的大小,代码不难,不做讲解。只有得到了这个box,接下来我们才能判断box里的点是否在三角形里面,实际上不要这个box也可以,直接对整个显示屏幕进行采样,但是这样会大大浪费掉计算机的性能,没必要。
2、遍历box里的所以像素点,这里使用整数索引即可。判断这些像素点是否在三角形内部
for (int i = minpoint.x(); i < maxpoint.x(); ++i)
for (int j = minpoint.y(); j < maxpoint.y(); ++j)
if (insideTriangle(x, y, vec))//在三角形里面
如果在三角形内部,那就需要计算出深度值,这里老师直接给了计算方法
std::tuple<float, float, float> temp =computeBarycentric2D(i, j, t.v);
float alpha = std::get<0>(temp);
float beta = std::get<1>(temp);
float gamma = std::get<2>(temp);
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;
关于computeBarycentric2D()
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector3f* v)
{
float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
return {c1,c2,c3};
}
忘了说判断像素点是不是在三角形里面的算法insideTriangle()
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 point = { x,y,0 };
Eigen::Vector3f vec0 = _v[1] - _v[0];//三条边向量
Eigen::Vector3f vec1 = _v[2] - _v[1];
Eigen::Vector3f vec2 = _v[0] - _v[2];
float a = ((point - _v[0]).cross(vec0)).z();//做叉积
float b = ((point - _v[1]).cross(vec1)).z();
float c = ((point - _v[2]).cross(vec2)).z();
if ((a > 0 && b > 0 && c > 0)||(a < 0 && b < 0 && c < 0))
{
//std::cout << "a:" << a << " b:" << b << " c:" << c << std::endl;
return true;//在三角形里面
}
else
{
return false;//不在
}
}
算法很简单,老师在上课的适合也说过,就是做叉积。
最后判断像素点的depth深度,比较和深度缓冲区里储存的数值的大小,要是更小,就更新深度缓冲区。
提高作业:
提高作业的要求是使用SSAA算法反走样,抗锯齿,就是改变一下rasterize_triangle()函数里面的部分代码,这里只贴主要的代码
if (insideTriangle(x, y, vec))//在三角形里面
{
std::tuple<float, float, float> temp = computeBarycentric2D(i, j, t.v);
float alpha = std::get<0>(temp);
float beta = std::get<1>(temp);
float gamma = std::get<2>(temp);
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;
depth = MIN(depth, z_interpolated);
color = color + t.getColor();
flag++;
}
}
if (flag > 0)//有像素点在三角形里面
{
if (depth < depth_buf[(get_index(i, j))])//离摄像机更近
{
set_pixel(Eigen::Vector3f(i, j, 1), color / 4.0);
depth_buf[get_index(i, j)] = depth;
}
}
}
其实SSAA算法就是找更多的检测点而已,把一个整数像素点又分为四个小的监测点,这样得到的四个点的color的平均值来对此像素着色,可以达到模糊边界,也就是抗锯齿的效果。