图形学基础 | 画直线算法

从零实现3D图像引擎:(2)画2D直线不简单

问题

如何在平面上画直线?
本来我以为画直线会很容易,随便拿个直线公式,遍历X求Y画出来不就完了么,但事实并非如此。
由于像素都必须是位于整像素点上.所以要将来连续的直线离散化
在这里插入图片描述

算法1 DDA

  • DDA算法主要是根据直线公式y = kx + b来推导出来的.
  • 其关键之处在于如何设定单位步进即一个方向的步进为单位步进,另一个方向的步进必然是小于1。
  • 缺陷: 由于有浮点数运算与取整,该算法不利于硬件实现
// DDA 数字微分分析
if (x1 == x2&&y1 == y2) {
		device_pixel(x1 + 0.5, y1 + 0.5, c);
		return;
	}

	int dx = x2 - x1, dy = y2 - y1;
	int step, k;
	float xIncrement, yIncrement, x = x1, y = y1;
	if (fabs(dx) > fabs(dy)) {
		step = fabs(dx);
	}
	else {
		step = fabs(dy);
	}
	xIncrement = (float)dx / (float)step;
	yIncrement = (float)dy / (float)step;
	device_pixel(x+0.5, y + 0.5, c);
	for (k = 0; k < step; k++) {
		x += xIncrement;
		y += yIncrement;
		device_pixel(x + 0.5, y + 0.5, c);
	}

算法2 Bresenham算法

  • Bresenham想法很简单,就是每X移动一个像素,则考虑Y应该是如何移动。
  • 该算法把直线分为两种:一种是斜率<1的线,即近X轴线。另一种是斜率>1的线,即近Y轴线。

在这里插入图片描述
Bresenham算法的核心就是,当X加1后,如何决定Y的移动。
显然可见,近X轴直线的dy<dx。
所以一个直观的想法是,保存一个误差累计变量,每当X加1,误差变量便累计增加一个dy。

  • 当累计误差小于等于dx时,Y不动,
  • 当累计的误差大于dx时,Y加1,同时把累计误差减掉一个dx。
    这样,算法将不停的将光栅线与实际线之间的误差减到最小。
// Bresenham 算法
int x=0, y=0, rem = 0;
if (x1 == x2&&y1 == y2) {
	device_pixel(x1, y1,c);
}
else if (x1 == x2) {
	int inc = (y1 <= y2) ? 1 : -1;
	for (y = y1; y != y2; y += inc) device_pixel(x, y,c);
	device_pixel( x2, y2, c);
}
else if (y1 == y2) {
	int inc = (x1 <= x2) ? 1 : -1;
	for (x = x1; x != x2; x += inc) device_pixel(x, y, c);
	device_pixel(x2, y2, c);
}
else {
	// 判断直线是近x轴还是近y轴
	int dx = (x1 < x2) ? x2 - x1 : x1 - x2;
	int dy = (y1 < y2) ? y2 - y1 : y1 - y2;
	if (dx >= dy) {
		// 靠近x轴
		if (x2 < x1) x = x1, y = y1, x1 = x2, y1 = y2, x2 = x, y2 = y;
		for (x = x1, y = y1; x <= x2; x++) {
			device_pixel(x, y, c);
			rem += dy;
			if (rem >= dx) {
				rem -= dx;
				y += (y2 >= y1) ? 1 : -1;
				device_pixel(x, y, c);
			}
		}
		device_pixel(x2, y2, c);
	}
	else {
		// 靠近y轴
		if(y2<y1) x = x1, y = y1, x1 = x2, y1 = y2, x2 = x, y2 = y;
		for (x = x1, y = y1; y <= y2; y++) {
			device_pixel(x, y, c);
			rem += dx;
			if (rem >= dy) {
				rem -= dy;
				x+=(x2 >= x1) ? 1 : -1;
				device_pixel(x, y, c);
			}
		}
		device_pixel(x2, y2, c);
	}
}

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/88903067