请用来检验答案是否正确
思路
-
判断点是否在三角形内
若P点在ABC构成的三角形内,ABC中的任意一点与P点都在另外两点构成的线段的同一侧。假设AB为边,判断C、P点是否在同一侧,可通过AB与AP的叉积、AB与AC的叉积(叉积计算出垂直于这两个向量的向量),判断结果是否同方向来判断是否在同一侧(可以通过点积结果是否大于0)。
-
计算Bounds
遍历三角形的三个点,取出最小和最大的x/y值,就是边界了。同时根据games101课上所说,将x,y分成一个个单位为1的格子,因此边界需要分别上下取整。
-
光栅化
遍历格子中心点,根据案例给出的方法计算出深度值,和深度缓存比较,更小的值就更新深度缓存,设置颜色缓存。如果是开启SSAA,就遍历中心点时候,不直接用小格子的中心点,而是将单位格子再拆分成四个格子,分别计算四个小格子是否三角形内。最后取四个小格子的最小深度值来做深度测试。 这里需要注意的是,采样颜色时针对不在三角形内的小格子的部分,要使用帧缓存中的值来参与计算,否则会出现黑边的问题
发现开启ssaa后性能好低,后面有空看看这块有没有什么优化方法
Code
计算点是否在三角形内:
// 判断c、p点是否在ab边的同一侧
static bool sameSide(Vector3f p, Vector3f a, Vector3f b, Vector3f c)
{
Vector3f ab = b - a;
Vector3f ac = c - a;
Vector3f ap = p - a;
return (ab.cross(ac)).dot(ab.cross(ap)) >= 0;
}
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]
Vector3f pos = {x, y, 1};
return sameSide(pos, _v[0], _v[1], _v[2]) &&
sameSide(pos, _v[2], _v[0], _v[1]) &&
sameSide(pos, _v[1], _v[2], _v[0]);
}
光栅化三角形:
// 获取三个里最小值
static int GetMin(float a, float b, float c){
return floor(std::min(a, std::min(b,c)));
}
// 获取三个里最大值
static int GetMax(float a, float b, float c){
return ceil(std::max(a, std::max(b,c)));
}
// Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// TODO : Find out the bounding box of current triangle.
int minx = GetMin(v[0][0], v[1][0], v[2][0]);
int maxx = GetMax(v[0][0], v[1][0], v[2][0]);
int miny = GetMin(v[0][1], v[1][1], v[2][1]);
int maxy = GetMax(v[0][1], v[1][1], v[2][1]);
// 是否开启反走样开关
bool super_sampling = true;
// iterate through the pixel and find if the current pixel is inside the triangle
for (int x = minx; x <= maxx; x++)
{
for (int y = miny; y <= maxy; y++)
{
// 开启反走样计算
if(super_sampling){
int count = 0;
float depth = std::numeric_limits<float>::infinity();
Vector2f midCube[4]{
{0.25, 0.25},
{0.25, 0.75},
{0.75, 0.25},
{0.75, 0.75}
};
for (int i = 0; i < 4; i++)
{
float posx = x + midCube[i][0];
float posy = y + midCube[i][1];
// insideTriangle 这里参数需要改为float或double,避免被强转int
if(insideTriangle(posx, posy, t.v)){
float alpha;
float beta;
float gamma;
auto zValue = computeBarycentric2D(posx, posy, t.v);
std::tie(alpha, beta, gamma) = zValue;
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 = std::min(depth, z_interpolated);
count++;
}
}
if (count>0) {
int index = get_index(x,y);
if(depth < depth_buf[index]){
Vector3f point = {x, y , depth};
// 避免黑线问题
Vector3f color = t.getColor() * count / 4.0 + (4 - count) * frame_buf[index] / 4.0;
depth_buf[index] = depth;
set_pixel(point, color);
}
}
}
else
{
float posx = x + 0.5;
float posy = y + 0.5;
// If so, use the following code to get the interpolated z value.
if(insideTriangle(posx, posy, t.v)){
float alpha;
float beta;
float gamma;
auto zValue = computeBarycentric2D(posx, posy, t.v);
std::tie(alpha, beta, gamma) = zValue;
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.
int index = get_index(x,y);
if(z_interpolated < depth_buf[index]){
Vector3f point = {x, y , z_interpolated};
Vector3f color = t.getColor();
depth_buf[index] = z_interpolated;
set_pixel(point, color);
}
}
}
}
}
}
未开启ssaa:
开启ssaa: