三角光栅化
通过之前的Bresenham画线算法,最简单的方式便是通过三组两两点的直线算法来实现画出三角形
在三角形之间通过平行的扫描线进行渲染
一个好的光栅化算法需要做到以下几点
- 简单快速
- 不会受到输入的数据顺序的影响
- 如果两个三角形有公共点,那么这两个三角形之间重合的部分不能有断点之类出现
算法思路
光栅化的过程,扫描线算法line sweeping:
- 将三角形的点根据y的值按照升序进行排列
- 将三角形分成平顶和平底两部分
- 扫描线进行渲染
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage& image, TGAColor color) { if (t0.y == t1.y && t0.y == t2.y)return;
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 - t1.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;
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); } } }
|
包围盒和重心坐标
重心坐标
在2D坐标系中,给定三角形ABC,存在一点p,有系数u,v使得
P = (1-u-v) * P1 + u * P2 + v * P3
通过连立方程组我们可以得到
- w1 = 1 - w2 - w3
- Px = w1 * P1x + w2 * P2x + w3 * P3x
- Py = w1 * P1y + w2 * P2y + w3 * P3y
进行变换得到
- w1 = 1 - w2 - w3
- Px - P3x = w1 * (P1x - P3x) + w2 * (P2x - P3x)
- Py - P3y = w1 * (P1y - P3y) + w2 * (P2y - P3y)
令C = P-P3, A = P1-P3, B = P2-P3, 可得
- Cx = w1 * Ax + w2 * Bx
- Cy = w1* Ay + w2 * By
- w3 = 1 - w1 - w2
最后解出方程组可得,三个解是与对应的三角形面积成比例的,因此可以先用叉乘计算三角形面积,再求面积的比值
算法思路
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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); 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])); } } }
|