博客仅用于个人记录学习过程中的想法和遇到的错误,可能不适合学习目的的阅读。
所有图片都来自现代计算机图形学入门-闫令琪
第一部分
要求是把两个三角形栅格化,填写一个判断点是否再三角形内部的函数和一个光栅化函数。
判断点在三角形内部只要用向量叉乘就能实现
判断点
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]
bool jpos = false;
bool jnega = false;
x += 0.25;
y += 0.25;
Eigen::Vector3f v01 = _v[1] - _v[0];
Eigen::Vector3f v12 = _v[2] - _v[1];
Eigen::Vector3f v20 = _v[0] - _v[2];
Eigen::Vector3f p = {x, y, 0};
Eigen::Vector3f p01 = p - _v[0];
Eigen::Vector3f p12 = p - _v[1];
Eigen::Vector3f p20 = p - _v[2];
if(v01.cross(p01)[2] < 0)
jnega = true;
else
jpos = true;
if(v12.cross(p12)[2] < 0)
jnega = true;
else
jpos = true;
if(v20.cross(p20)[2] < 0)
jnega = true;
else
jpos = true;
if(jnega&&jpos)
return false;
else
return true;
}
光栅化
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
int bx1, bx2, by1, by2;
bx1 = min(t.v[0][0], min(t.v[1][0], t.v[2][0]));
bx2 = max(t.v[0][0], max(t.v[1][0], t.v[2][0]));
by1 = min(t.v[0][1], min(t.v[1][1], t.v[2][1]));
by2 = max(t.v[0][1], max(t.v[1][1], t.v[2][1]));
Eigen::Vector3f p;
clear(Buffers::Depth);
for(int i = bx1; i <= bx2; ++i)
{
for(int j = by1; j <= by2; ++j)
{
if(insideTriangle(i, j, t.v))
{
auto[alpha, beta, gamma] = computeBarycentric2D(i, j, 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(z_interpolated<depth_buf[get_index(i, j)])
{
depth_buf[get_index(i, j)] = z_interpolated;
p<< i, j, 0;
set_pixel(p, t.getColor());
}
}
}
}
}
找到一个能刚好包含三角形的矩形,遍历判断每个矩形上的点是否在三角形内部然后求出该点插值的z坐标,记录到深度,在填色即可。
提高部分
要求做msaa的抗锯齿
直接上代码
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
float sampling[5][2] = {{-0.25, -0.25}, {-0.25, 0.25}, {0.25, -0.25}, {0.25, 0.25}};
float Minx, Miny, Maxx, Maxy;
Minx = std::min(t.v[0][0], std::min(t.v[1][0], t.v[2][0]));
Miny = std::min(t.v[0][1], std::min(t.v[1][1], t.v[2][1]));
Maxx = std::max(t.v[0][0], std::max(t.v[1][0], t.v[2][0]));
Maxy = std::max(t.v[0][1], std::max(t.v[1][1], t.v[2][1]));
Maxx = judgeint(Maxx);
Minx = judgeint(Minx);
Maxy = judgeint(Maxy);
Miny = judgeint(Miny);
for(int i = Miny; i <= Maxy; ++i)
{
for(int j = Minx; j <= Maxx; ++j)
{
bool flag = false;
Eigen::Vector3f colorsum(0 ,0 ,0);
colorsum[0] += color_list[get_index(j, i)*12+0];
colorsum[1] += color_list[get_index(j, i)*12+1];
colorsum[2] += color_list[get_index(j, i)*12+2];
for(int k = 0; k < 4; ++k)
{
float x = j+sampling[k][0]+0.5;
float y = i+sampling[k][1]+0.5;
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;
if(insideTriangle(x, y, t.v))
{
if(z_interpolated<sample_list[get_index(j, i)*4+k])
{
flag = true;
sample_list[get_index(j, i)*4+k] = z_interpolated;
colorsum += t.getColor()/(float)4;
colorsum[0] -= color_list[get_index(j, i)*12+0]/(float)4;
colorsum[1] -= color_list[get_index(j, i)*12+1]/(float)4;
colorsum[2] -= color_list[get_index(j, i)*12+2]/(float)4;
}
}
}
Vector3f Mindex(j, i, 0);
if(flag)
{
set_pixel(Mindex, colorsum);
color_list[get_index(j, i)*12+0] = (t.getColor())[0];
color_list[get_index(j, i)*12+1] = (t.getColor())[1];
color_list[get_index(j, i)*12+2] = (t.getColor())[2];
}
}
}
}
一开始做的时候两个三角形重叠部分有黑边主要是几个原因
- 插值时候没有对每个采样点插值,同样没有记录每个采样点的深度,这种情况下,当后画的三角形离的更远的时候,两三角形边界处会出现黑边。因为即使边界上某一个采样点不在前三角形上,由于只记录了像素的深度,该采样点还是被判断为在前三角形上,这样绘制后三角形的时候该点被当作是前三角形的部分而不填色,就出现了黑边
- 没有记录背景色,出现深色边。背景色不记录的话默认就是黑色,然后再绘制第二个三角形的时候边界的像素点颜色在采样的时候会混入黑色,所以出现了不是纯黑的深色边。