以下摘抄自本人的计算机图形学上机报告
实验内容及要求:
- 理解并掌握中点画线法、Bresenham算法、扫描线法和重心坐标法的基本原理和算法步骤。
- 使用编程语言C++实现上述算法,并编写相应的代码。
- 对于直线绘制算法,要求能够绘制水平、垂直、斜向等不同方向的直线,并观察绘制结果是否符合预期。
- 对于三角形填充绘制算法,要求能够填充任意位置和大小的三角形,并观察填充结果是否完整且没有漏填或重填的情况。
- 编写实验报告,包括实验目的、实验原理、实验步骤、实验结果和实验总结等部分,对实验过程和结果进行详细的描述和分析。
算法思想
本次直线的绘制采用的是中点画线法。
中点画线法的算法原理:
设为 P1与P2之中点,Q为理想直线与
垂线的交点。将Q与M的y坐标进行比较。当M在Q的下方,则P2应为下一个像素点;当M在Q的上方,则P1应为下一个像素点
三角形的绘制采用的是扫描线法和重心坐标法。
扫描线法的算法原理:
- 顶点排序:首先,根据 y 坐标对三角形的三个顶点进行排序。确保 ta 是最下面的顶点,tc 是最上面的顶点,而 tb 则位于中间。
- 计算总高度:计算三角形在 y 轴上的总高度,即 tc.y - ta.y。
- 绘制底部条带:从 ta 到 tb 的 y 坐标范围内,循环遍历每一行(y 值)。对于每一行,计算 A 和 B 两个点的 x 坐标。这两个点位于当前行的扫描线上,并且与三角形的两边相交。然后,从 A 点到 B 点填充像素。如果 A 的 x 坐标大于 B 的 x 坐标,则交换 A 和 B,以确保填充的顺序是正确的。
- 绘制顶部条带:从 tb 到 tc 的 y 坐标范围内,重复与底部条带相同的步骤。
中心坐标法的算法原理:
1.确定边界:
x_max 和 x_min 是三角形在x轴上的最大和最小坐标。
y_max 和 y_min 是三角形在y轴上的最大和最小坐标。
这些边界坐标用来确定需要遍历的像素范围。
2.遍历像素:
使用两个嵌套的 for 循环遍历从 y_min 到 y_max 的每一行,以及从 x_min 到 x_max 的每一列。
3.计算重心坐标:
对于每一个像素点 (x, y),计算其相对于三角形三个顶点的重心坐标 alpha 和 beta。
4.判断像素点是否在三角形内部:
如果 alpha, beta 和 1 - alpha - beta 都大于0,则说明像素点 (x, y) 位于三角形内部
5.绘制像素:
如果像素点 (x, y) 在三角形内部,则调用 draw_pixel 函数将其绘制为给定的颜色 color。
代码说明及实验结果
中点画线算法:
void draw_line(int x1, int y1, int x2, int y2, const TGAColor& color)
{
int a, b, delta1, delta2, d, x, y;
a = y1 - y2;
b = x2 - x1;
d = 2 * a + b;
delta1 = 2 * a;
delta2 = 2 * (a + b);
x = x1;
y = y1;
draw_pixel(x, y, color);
while (x < x2)
{
if (d < 0)
{
x++;
y++;
d += delta2;
}
else {
x++;
d += delta1;
}
draw_pixel(x, y, color);
}
}
扫描线算法:
void draw_triange2(vec2i ta, vec2i tb, vec2i tc, const TGAColor& color)
{
if (ta.y > tb.y) std::swap(ta, tb);
if (ta.y > tc.y) std::swap(ta, tc);
if (tb.y > tc.y) std::swap(tb, tc);
int total_height = tc.y - ta.y;
for (int y = ta.y; y < tb.y; ++y) {
int segment_height = tb.y - ta.y;
float alpha = (float)(y - ta.y) / total_height;
float beta = (float)(y - ta.y) / segment_height;
vec2i A = ta + (tc - ta) * alpha;
vec2i B = ta + (tb - ta) * beta;
if (A.x > B.x) std::swap(A, B);
for (int j = A.x; j <= B.x; ++j) {
draw_pixel(j, y, color);
}
}
for (int y = tb.y; y <= tc.y; ++y) {
int segment_height = tc.y - tb.y;
float alpha = (float)(y - ta.y) / total_height;
float beta = (float)(y - tb.y) / segment_height;
vec2i A = ta + (tc - ta) * alpha;
vec2i B = tb + (tc - tb) * beta;
if (A.x > B.x) std::swap(A, B);
for (int j = A.x; j <= B.x; ++j) {
draw_pixel(j, y, color);
}
}
}
重心坐标算法:
void draw_triange1(vec2i ta, vec2i tb, vec2i tc, const TGAColor& color)
{
float x,y,alpha, beta;
int x_max = max(ta.x, max(tb.x, tc.x));
int x_min = min(ta.x, min(tb.x, tc.x));
int y_max = max(ta.y, max(tb.y, tc.y));
int y_min = min(ta.y, min(tb.y, tc.y));
for (y = y_min; y <= y_max; y++) {
for (x = x_min; x <= x_max; x++)
{
alpha = (-(x - tb.x) * (tc.y - tb.y) + (y - tb.y) * (tc.x - tb.x)) / (-(ta.x - tb.x) * (tc.y - tb.y) + (ta.y - tb.y) * (tc.x - tb.x));
beta = (-(x - tc.x) * (ta.y - tc.y) + (y - tc.y) * (ta.x - tc.x)) / (-(tb.x - tc.x) * (ta.y - tc.y) + (tb.y - tc. y) * (ta.x - tc.x));
if (alpha > 0 && beta >0 && 1 - alpha - beta >0)
{
draw_pixel(x, y, color);
}
}
}
}