有了上一节的基础,我们需要计算空间中一点 P相对于三角形的重心坐标。
然而,空间中的点可以说是无穷无尽的,为了缩小计算的范围,我们定义一个矩形称为包围盒,这个矩形将把三角形包住。

为了确定矩形在空间中的坐标,我们定义矩形的左上角点,和右下角点。将左上角点初始化为无穷大,将右下角点初始化为无穷小。
通过遍历三角形的x、y分量:

void triangle_barycentricCoordinates(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color)
{
// 定义包围框的左上角点和右下角点
Vec2f leftTopPoint = Vec2f(numeric_limits<float>::max(), numeric_limits<float>::max());
Vec2f rightBottomPoint = Vec2f(numeric_limits<float>::min(), numeric_limits<float>::min());
// 计算3个顶点所构成三角形的包围框
for (int i = 0; i < 3; i++)
{
if (pts[i].x < leftTopPoint.x)
{
leftTopPoint.x = pts[i].x;
}
if (pts[i].y < leftTopPoint.y)
{
leftTopPoint.y = pts[i].y;
}
if (pts[i].y > rightBottomPoint.y)
{
rightBottomPoint.y = pts[i].y;
}
if (pts[i].x > rightBottomPoint.x)
{
rightBottomPoint.x = pts[i].x;
}
}
// 遍历包围框的点,使用重心性质来计算每个点是否在三角形内部
Vec3f P;
for (P.x = leftTopPoint.x; P.x <= rightBottomPoint.x; P.x++)
{
for (P.y = leftTopPoint.y; P.y <= rightBottomPoint.y; P.y++)
{
// 计算P点在三角形内的重心坐标
Vec3f barycentricP = barycentric_(pts, P);
// 当重心坐标任一分量小于0,说明P点在三角形外部
if (barycentricP.x < 0 || barycentricP.y < 0 || barycentricP.z < 0)
{
continue;
}
// 将三个顶点的z坐标值按照重心坐标比例加权相加,得到该点在三角形内的深度值
P.z = 0;
for (int i = 0; i < 3; i++)
{
P.z += pts[i].z * barycentricP.x;
P.z += pts[i].z * barycentricP.y;
P.z += pts[i].z * barycentricP.z;
}
// 将缓存中的深度值与该点的深度值进行比较
if (zbuffer[int(P.x + P.y * width)] < P.z)
{
zbuffer[int(P.x + P.y * width)] = P.z;
image.set(P.x, P.y, color);
}
}
}
}
此代码接受一个包含三个 3D 点的数组,表示三角形的顶点,一个一维浮点值数组,表示 z 缓冲区、一个 TGAImage 对象和一个 TGAColor 对象。它使用重心坐标来确定三角形边界框内的哪些像素在三角形内,然后更新 z 缓冲区并将 TGAImage 对象中的相应像素设置为提供的颜色。
代码首先通过找到三个顶点的最小和最大 x 和 y 坐标来计算三角形的边界框。然后,它遍历此边界框内的所有像素,并使用 barycentric_() 函数(未显示)计算每个像素相对于三角形三个顶点的重心坐标。如果任何重心坐标为负,则该像素位于三角形之外并被跳过。否则,像素的 z 坐标将使用重心坐标计算为三个顶点的 z 坐标的加权平均值。如果计算出的 z 坐标大于像素的 z 缓冲区值,则更新 z 缓冲区值并将 TGAImage 对象中的像素颜色设置为提供的颜色。