我正在GitHub上学习一门短期课程“How OpenGL works: software rendering in 500 lines of code”。在lesson 2中,作者教我们如何用颜色填充三角形。他提出了两种方法:
枚举三角形内的所有水平线段,并绘制这些线段。作者的代码如下。
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) {
if (t0.y==t1.y && t0.y==t2.y) return; // I dont care about degenerate triangles
// sort the vertices, t0, t1, t2 lower−to−upper (bubblesort yay!)
if (t0.y>t1.y) std::swap(t0, t1);
if (t0.y>t2.y) std::swap(t0, t2);
if (t1.y>t2.y) std::swap(t1, t2);
int total_height = t2.y-t0.y;
for (int i=0; i<total_height; i++) {
bool second_half = i>t1.y-t0.y || t1.y==t0.y;
int segment_height = second_half ? t2.y-t1.y : t1.y-t0.y;
float alpha = (float)i/total_height;
float beta = (float)(i-(second_half ? t1.y-t0.y : 0))/segment_height; // be careful: with above conditions no division by zero here
Vec2i A = t0 + (t2-t0)*alpha;
Vec2i B = second_half ? t1 + (t2-t1)*beta : t0 + (t1-t0)*beta;
if (A.x>B.x) std::swap(A, B);
for (int j=A.x; j<=B.x; j++) {
image.set(j, t0.y+i, color); // attention, due to int casts t0.y+i != A.y
}
}
}
找到三角形的边界框。枚举边界框中的所有点,并使用重心坐标检查该点是否在三角形内。如果该点位于三角形中,则用颜色填充该点。作者的代码如下。
Vec3f barycentric(Vec2i *pts, Vec2i P) {
Vec3f u = cross(Vec3f(pts[2][0]-pts[0][0], pts[1][0]-pts[0][0], pts[0][0]-P[0]), Vec3f(pts[2][1]-pts[0][1], pts[1][1]-pts[0][1], pts[0][1]-P[1]));
if (std::abs(u[2])<1) return Vec3f(-1,1,1); // triangle is degenerate, in this case return smth with negative coordinates
return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
}
void triangle(Vec2i *pts, TGAImage &image, TGAColor color) {
Vec2i bboxmin(image.get_width()-1, image.get_height()-1);
Vec2i bboxmax(0, 0);
Vec2i clamp(image.get_width()-1, image.get_height()-1);
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
bboxmin[j] = std::max(0, std::min(bboxmin[j], pts[i][j]));
bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j]));
}
}
Vec2i P;
for (P.x=bboxmin.x; P.x<=bboxmax.x; P.x++) {
for (P.y=bboxmin.y; P.y<=bboxmax.y; P.y++) {
Vec3f bc_screen = barycentric(pts, P);
if (bc_screen.x<0 || bc_screen.y<0 || bc_screen.z<0) continue;
image.set(P.x, P.y, color);
}
}
}
作者在第2课结束时选择了第二种方法,但我无法理解为什么。问题的关键在于效率,还是因为第二种方法更容易理解?
答案 0 :(得分:2)
重心坐标用于内插或涂抹&#34;三角形上三角形每个顶点的值。例如:如果我定义一个三角形ABC,我可以分别给每个顶点一个颜色,红色,绿色和蓝色。然后当我填写三角形时,我可以使用重心坐标(alpha,beta,gamma)来获得线性组合P = alpha * Red + beta * Blue + gamma * Green来确定三角形内某点的颜色应该是。
此流程经过高度优化,内置于GPU硬件中。您可以涂抹任何您喜欢的值,包括法向量(通常用于每像素照明计算),因此这是一个非常有用的操作。
当然,我不知道你的老师在想什么,但我猜测在未来的课程中他们可能会谈论这个问题,所以第二种算法自然会引发讨论。