计算机图形学基础 : 基本图形生成算法之圆的扫描转换


圆的扫描转换

前置声明:我们只考虑圆心在原点,半径为整数R的圆x^2 + y^2 = R^2。 对于中心不在原点的圆,可以通过相应的平移变换来转化为圆心在原点的圆。

中点画圆法

我们考虑中心在原点,半径为R的圆的第二8分圆,如下有图所示:

计算机图形学基础 : 基本图形生成算法之圆的扫描转换_第1张图片


讨论从(0, R) 到 (R/√2, R/√2)顺时针的确定最佳逼近于圆弧的像素序列。

假设x坐标为Xp的像素中与圆弧最近者已确定为P(xp, yp),那么下一个像素只能是正右方的P1(xp + 1, yp) 或者是右下方的 P2(xp +1, yp - 1)两者之一。
构造函数: F(x, y) = x^2 + y^2 - R^2

计算机图形学基础 : 基本图形生成算法之圆的扫描转换_第2张图片

类似于中点画线法, M坐标为(xp + 1, yp - 0.5),F(M) < 0, M在圆内,则P1离圆弧更近,F(M) > 0,M在圆外,则P2离圆弧更近,F(M) = 0,约定取P2。

构造判别式: d = F(M) = F(xp + 1, yp - 0.5) = (xp + 1)^2 + (yp - 0.5)^2 - R^2.

如果d < 0 则P1 为下一个像素点,而且再下一个像素的判别式为:
d = F(M) = F(xp + 1 + 1, yp - 0.5) = (xp + 1 + 1)^2 + (yp - 0.5)^2 - R^2 = d + 2*xp + 3.
因此,沿正右方,d的增量为 2*xp + 3。

如果d > 0 则P2 为下一个像素点,而且再下一个像素的判别式为:
d = F(M) = F(xp + 1 + 1, yp - 1 - 0.5) = (xp + 1 + 1)^2 + (yp - 1 - 0.5)^2 - R^2 = d + (2*xp + 3) + (-2*yp + 2).
因此,沿正右方,d的增量为 2*(xp - yp) + 5。

d0 = F(1, R - 0.5) = 1.25 - R.

可以看到,d的初始值使用了浮点数来表示,会了简化算法,摆脱浮点数使用整数,我们使用e = d - 0.25代替d。因此,初始化运算d = 1.25 - r对应于e = 1 - r,判别式d < 0 对应于e < -0.25,由于e的值为整数,因此 e < -0.25 等价于 e < 0,算法中e仍用d 来表示,根据上述分析,中点画圆法的算法如下:

void MidPointCircle(int r, int color)
{
	int x, y, d;
	x = 0;
	y = r;
	d = 1 - r;
	DrawPixel(x, y, color);
	while (x < y)
	{
		if (d < 0)
		{
			d += 2 * x + 3;
			++x;
		}
		else
		{
			d += 2 * (x - y) + 5;
			++x;
			--y;
		}
		DrawPixel(x, y, color);
	}
}


Bresenham画圆算法

讨论圆心在原点,半径为整数R的第一个4分圆。取(0, R)为起点,按顺时针方向生成圆。

计算机图形学基础 : 基本图形生成算法之圆的扫描转换_第3张图片

如上图右,假定(x, y)为已经确定的点,那么下一个像素点可能为正右方的H(x + 1, y)、右下方D(x + 1, y - 1)或者V(x, y - 1)。
理想圆弧与这三个候选点之间的关系有下列五点情况:
1. H、D、V全在圆内
2. H在圆外,D、V在圆内
3. D在圆上,H在圆外,V在圆内
4. H、D在圆外,V在圆内
5. H、D、V全在圆外

上述三点到圆心的距离平方与圆弧上一点到圆心的距离平方之差分别为:
△H = (x + 1)^2 + y^2 - R^2
D = (x + 1)^2 + (y - 1)^2 - R^2
V = x^2 + (y - 1)^2 - R^2

如果△D < 0,那么右下方像素D在圆内,圆弧与候选点的关系只可能是 1 和 2的情形。显然,这时最逼近圆弧的像素只可能是H或者D这两个像素之一。
令 △HD = | △H | - | △D | = | (x + 1)^2 + y^2 - R^2 | - | (x + 1)^2 - (y - 1)^2 - R^2 |, 若△HD < 0,则圆到正右方像素H的距离小于圆到D的距离,这时应取H为下一个像素;若△HD > 0,则选取D为下一个像素点,当△HD = 0时,约定取正右方像素H。

对于情形2,H总在圆外,D总在圆内,因此△H >= 0, △D < 0,所以△HD可以简化为:△HD = △H + △D = 2*△D + 2*y - 1.
对于情形1,这时,H、D都在圆内,而在这段圆弧上,y是x的单调递减函数,所以只能取H为下一像素。 由于△H < 0且 △D < 0,因此2*△D + 2*y - 1 < 0。 
可见△D < 0的情况下,若2(△D + y) - 1 <= 0,则应取H为下一像素,否则应取D为下一像素。

再讨论△D > 0的情况,这时,右下方像素D在圆外,最佳逼近圆弧的像素只可能是D与V二者之一。

先考虑情形4,令△DV = | △D | - | △V | = | (x + 1)^2 + (y - 1)^2 - R^2 | - | x^2 + (y - 1)^2 - R^2 |
如果△DV < 0,即圆到右下方像素距离较小,应取右下方像素D;如果△DV > 0,圆到正下方像素V的距离较小,应取V;当△DV = 0时,约定取右下方像素D。
由于右下方像素D在圆外,而正下方像素V在圆内,所以△D >= 0, △v < 0 因此:
DV = △D + △V = 2(△D - x) - 1

对于情形5,D和V都在圆外,显然应取V作为下一个像素。这时△D > 0且 △V > 0,因此 2(△D - x) - 1 > 0
可见,在△D > 0的情况下,若2(△D - x) - 1 <= 0,应取D为下一像素,否则去V作为下一像素。

归纳一下,计算下一像素的算法为:
当△D > 0, 若△DV <= 0,则取D,否则取V
当△D < 0, 若△HD <= 0,则取H,否则去D
当△D = 0,取D


△HD和△DV 可由△D推算出来:
1. 考虑下一个像素为H的情况,像素H(x', y') = (x + 1, y),误差项为: △D' = ((x + 1) + 1)^2 + (y + 1)^2 - R^2 = △D + 2(x + 1) + 1 = △D + 2*x' + 1
2. 再考虑下一个像素为D的情况,像素D(x', y') = (x + 1, y - 1), △D' = △D + 2x' -2y' + 2
3. 下一个像素为V的情况,像素V(x', y') = (x, y - 1),△D‘ = △D - 2y' = 1

综上所述,完整的Bresenham算法为:

void BresenhamCircle(int r, int color)
{
	int x, y, delta, delta1, delta2, direction;
	x = 0;
	y = r;
	delta = 2*(1-r); // △D的初始值
	while( y >= 0)
	{
		DrawPixel(x, y, color);
		if (delta < 0)
		{
			delta1 = 2 * (delta + y) - 1;
			if (delta1 <= 0) 
				direction = 1;
			else
				direction = 2;
		}
		else if (delta > 0)
		{
			delta2 = 2 * (delta - x) - 1;
			if (delta2 <= 0)
				direction = 2;
			else
				direction = 3;
		}
		else 
			direction = 3;

		switch(direction)
		{
			case 1:
				++x;
				delta += 2 * x + 1;
				break;
			case 2:
				++x;
				--y;
				delta += 2 * (x - y + 1);
				break;
			case 3:
				--y;
				delta += (-2 * y + 1);
				break;
		}
	}
}


你可能感兴趣的:(3D计算机图形学&OpenGL)