【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战

个人主页:Sarapines Programmer
 系列专栏:《图形学 | 图像解码》
⏰诗赋清音:云生高巅梦远游, 星光点缀碧海愁。 山川深邃情难晤, 剑气凌云志自修。

目录

1. 初识模式识别

2. 开发环境的使用及基本图形生成

2.1 开发环境及实现

2.2 实验目的

2.3 实验要求

2.4 实验原理

2.4.1 DDA算法画直线

2.4.2 Bresenham算法画直线

2.4.3 DDA算法画圆

2.4.4 Bresenham算法画圆

2.5 实验步骤

2.5.1 DDA算法代码实现画直线

2.5.2 Breasenham 算法实现画直线

2.5.3 DDA算法代码实现画圆

2.5.4 Breasenham算法代码实现画圆

2.6 研究体会

总结


1. 初识模式识别

图形学技术是一门涉及计算机图形和图像处理的学科,其目标是通过算法和数学模型来创建、处理和呈现图形和图像。这项技术的应用范围非常广泛,涵盖了许多领域,包括计算机游戏、虚拟现实、计算机辅助设计(CAD)、医学图像处理、动画制作等。

以下是图形学技术的一些关键方面:

  1. 图形生成和渲染: 图形学技术用于生成和呈现视觉图像。这包括三维图形的创建、光照、阴影、颜色和纹理等方面的处理,以产生逼真的图形。

  2. 计算机辅助设计(CAD): 在工程学和设计领域,图形学技术被广泛用于创建和编辑数字化的设计图纸,促进设计过程的可视化和交互。

  3. 计算机游戏和虚拟现实: 图形学技术是游戏开发和虚拟现实领域的核心。它用于创建游戏中的角色、场景、特效以及虚拟现实环境,提供沉浸式的视觉体验。

  4. 医学图像处理: 在医学领域,图形学技术被用于处理和呈现医学图像,如CT扫描、MRI等,以协助医生进行诊断和手术规划。

  5. 动画制作: 图形学技术是制作动画的关键。通过在计算机上生成图形帧并进行渲染,动画制作得以实现。

  6. 图像处理: 图形学技术也包括对静态图像的处理,如图像编辑、滤镜应用、图像合成等。

在图形学技术的发展中,硬件加速、实时渲染、虚拟现实和增强现实等方面的创新不断推动着图形学的前沿。这门技术为数字世界的可视化和交互提供了强大的工具和方法。


2. 开发环境的使用及基本图形生成

2.1 开发环境及实现

  • 语言: C++
  • 平台: Microsoft Visual Studio 2022

2.2 实验目的

  1. 熟悉开发环境。
  2. 生成基本图形如直线和二次曲线。
  3. 掌握计算机生成直线以及修改直线属性的方法。

2.3 实验要求

  1. 熟悉 Microsoft Visual Studio 2022 开发环境。
  2. 使用 DDA 算法和 Bresenham 算法分别生成直线和圆。

2.4 实验原理

2.4.1 DDA算法画直线

DDA是数字微分分析式(Digital Differential Analyzer)的缩写。已知直线两端点(x1,y1)、(x2,y2),则斜率m为:

m = (y2-y1)/(x2-x1)= Dx/Dy;

直线中的每一点坐标都可以由前一点坐标变化一个增量(Dx, Dy)而得到,垠)育v1["vI~5,L,`?!_[d即表示为递归式:

xi+1=xi+Dx   yi+1=yi+Dy

递归式的初值为直线的起点(x1, y1),这样,就可以用加法来生成一条直线。


2.4.2 Bresenham算法画直线

本算法由Bresenham在1965年提出。设直线从起点(x1, y1)到终点(x2, y2)。直线可表示为方程y=mx+b。其中

b = y1 - m * x1,

m = (y2-y1)/(x2-x1)=dy/dx

我们的讨论先将直线方向限于1a象限在这种情况下,当直线光栅化时,x每次都增加1个单元,即

xi+1=xi+1。而y的相应增加应当小于1。为了光栅化,yi+1只可能选择如下两种位置之一。

yi+1的位置选择yi+1=yi 或者 yi+1=yi+1。选择的原则是看精确值y与yi及yi+1的距离d1及d2的大小而定。计算式为:

y=m(xi+1)+b

d1=y-yi

d2=yi+1-y

如果d1-d2>0,则yi+1=yi+1,否则yi+1=yi。因此算法的关键在于简便地求出d1-d2的符号。将式(2.1.1)、(2.1.2)、(2.1.3)代入d1-d2,得

d1-d2=2y-2yi-1=2(dy/dx) (xi+1)-2yi+2b-1

用dx乘等式两边,并以Pi=dx(d1-d2)代入上述等式,得

Pi=2xidy-2yidx+2dy+dx(2b-1)

d1-d2是我们用以判断符号的误差。由于在1a象限,dx总大于0,所以Pi仍旧可以用作判断符号的误差。Pi-1为:

Pi+1=Pi+2dy-2dx(yi+1-yi)

误差的初值P1,可将x1, y1,和b代入式(2.1.4)中的xi, yi而得到:

P1=2dy-dx

综述上面的推导,第1a象限内的直线Bresenham算法思想如下:

1.画点(x1, y2); dx=x2-x1; dy=y2-y1;

计算误差初值P1=2dy-dx; i=1;

2.求直线的下一点位置:

xi+1=xi+1;

if Pi>0 则yi+1=yi+1;

否则yi+1=yi;

3.画点(xi+1, yi-1);

4.求下一个误差Pi+1;

if Pi>0 则Pi+1=Pi+2dy-2dx;

否则Pi+1=Pi+2dy;

5.i=i+1; if i


2.4.3 DDA算法画圆

假定某点Pi(xi, yi)已经是该圆弧上最接近实际圆弧的点,那么Pi的下一个点只可能是正右方的P1或右下方的P2两者之一

构造判别函数:

F(x, y)= x2 + y2 – R2

当F(x, y)= 0,表示点在圆上,当F(x, y)> 0,表示点在圆外,当F(x, y)< 0,表示点在圆内。如果M是P1和P2的中点,则M的坐标是(xi + 1, yi – 0.5),当F(xi + 1, yi – 0.5)< 0时,M点在圆内,说明P1点离实际圆弧更近,应该取P1作为圆的下一个点。同理分析,当F(xi + 1, yi – 0.5)> 0时,P2离实际圆弧更近,应取P2作为下一个点。当F(xi + 1, yi – 0.5)= 0时,P1和P2都可以作为圆的下一个点,算法约定取P2作为下一个点。

现在将M点坐标(xi + 1, yi – 0.5)带入判别函数F(x, y),得到判别式d:

d = F(xi + 1, yi – 0.5)= (xi + 1)2 + (yi –0.5)2 – R2

若d < 0,则取P1为下一个点,此时P1的下一个点的判别式为:

展开后将d带入可得到的递推关系:d’ = d + 2xi + 3

若d > 0,则取P2为下一个点,此时P2的下一个点的判别式为:

d’ = F(xi + 2, yi – 1.5)= (xi + 2)2 + (yi –1.5)2 – R

展开后将d带入可得到判别式的递推关系:d’ = d + 2(xi - yi) + 5

特别的,在第一个象限的第一个点(0, R)时,可以推倒出判别式d的初始值d0:

d0 = F(1, R – 0.5) = 1 – (R – 0.5)2 –R2 = 1.25 – R


2.4.4 Bresenham算法画圆

Bresenham算法画圆生成算法思路如下:

①求误差初值,p0=3- 2r,i=1,画点(0,r)

②求下一个点的y坐标,其中xi+1=xi+1,如果pi<0则yi+1=yi,否则yi+1=yi-1

③画点(xi+1,yi+1)

④计算下一个误差,如果pi<0则pi+1=pi+4xi+6,否则pi+1=pi+4(xi-yi)+10

⑤i=i+1,如果x=y则结束,否则返回步骤②。


2.5 实验步骤

(1) 在Microsoft Visual Studio 2022环境下创建名为BmpRead的MFC应用程序工程(单文档)

(2)编程实现DDA算法和算法画直线,同时利用两种算法画圆。


2.5.1 DDA算法代码实现画直线
#include 
#include 
#include 
#include 
#include 
#include 

void DDA(int X0, int Y0, int Xn, int Yn)
{
	int dx = Xn - X0;
	int dy = Yn - Y0;
	int steps, direction;
	float xIncrement, yIncrement;
	float x = X0, y = Y0;
	if (abs(dx) > abs(dy))
	{
		steps = abs(dx);
		direction = 0;
	}
	else
	{
		steps = abs(dy);
		direction = 1;
	}
	xIncrement = float(dx) / float(steps);
	yIncrement = float(dy) / float(steps);
	//画点
	glBegin(GL_POINTS);
	for (int k = 0; k <= steps; ++k)
	{
		if (direction == 0)
		{
			glVertex2i(int(x), int(y + 0.5));
		}
		else
		{
			glVertex2i(int(x + 0.5), int(y));
		}
		x += xIncrement;
		y += yIncrement;
	}
	glEnd();
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	DDA(0, 0, 800, 1000);//调用函数
	glFlush();
}
void draw_pixel(int ix, int iy)
{
	glBegin(GL_POINTS);
	glVertex2i(ix, iy);
	glEnd();
}

void myinit()
{
	glClearColor(1.0, 0.8, 1.0, 1.0);
	glColor3f(0.0, 0.5, 0.5);
	glPointSize(1.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, 1000.0, 0.0, 1000.0);
}
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(600, 500);
	glutInitWindowPosition(150.0, 150.0);
	glutCreateWindow("DDA画直线");
	glutDisplayFunc(display);
	myinit();
	glutMainLoop();
}

运行结果:

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战_第1张图片


2.5.2 Breasenham 算法实现画直线

#include 
#include 
#include 
#include 
#include 
#include 

void Bresenham(int x0, int y0, int x1, int y1) {
	void draw_pixel(int, int);
	int dx = abs(x1 - x0), dy = abs(y1 - y0), p = 2 * dy - dx;
	int Dy2 = 2 * dy, Dx2 = 2 * dy - 2 * dx;
	int x, y;
	if (x0 > x1) {
		x = x1; y = y1;
		x1 = x0;
	}
	else {
		x = x0;
		y = y0;
	}
	draw_pixel(x, y);
	while (x < x1) {
		x++;
		if (p < 0)
			p += Dy2;
		else {
			y++;
			p += Dx2;
			draw_pixel(x, y);
		}
	}
}


void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	Bresenham(0, 0, 800, 800);//调用函数
	glFlush();
}
void draw_pixel(int ix, int iy)
{
	glBegin(GL_POINTS);
	glVertex2i(ix, iy);
	glEnd();
}

void myinit()
{
	glClearColor(1.0, 0.8, 1.0, 1.0);
	glColor3f(0.0, 0.5, 0.5);
	glPointSize(1.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, 1000.0, 0.0, 1000.0);
}
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(600, 500);
	glutInitWindowPosition(150.0, 150.0);
	glutCreateWindow("Bresenham算法画直线");
	glutDisplayFunc(display);
	myinit();
	glutMainLoop();
}

运行结果:

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战_第2张图片


2.5.3 DDA算法代码实现画圆
#include
#include
#include
#include
#include
#include
#include
using namespace std;


/*中点画圆*/
void MidpointCircle(int x0, int y0, int r, int color)
{
	int x = 0, y = r;
	float d = 5.0 / 4 - r;
	while (x <= y) {
		putpixel(x0 + x, y0 + y, color);
		putpixel(x0 + x, y0 - y, color);
		putpixel(x0 - x, y0 + y, color);
		putpixel(x0 - x, y0 - y, color);
		putpixel(x0 + y, y0 + x, color);
		putpixel(x0 + y, y0 - x, color);
		putpixel(x0 - y, y0 + x, color);
		putpixel(x0 - y, y0 - x, color);
		if (d < 0)
			d += x * 2.0 + 3;
		else {
			d += 2.0 * (x - y) + 5; y--;
		}
		x++;
	}
}


void main()
{
	int x0, y0, x1, y1;
	initgraph(640, 480);
	setbkcolor(YELLOW);
	cleardevice();
	MidpointCircle(300, 200, 90, BLACK);

	_getch();
	closegraph();
}


运行结果:

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战_第3张图片


2.5.4 Breasenham算法代码实现画圆
#include  
#include
#include
#include 
using namespace std;
GLfloat pointsize = 1.0f;
void Bresenham(int x0,int y0,GLint R) {
	int x1 = x0, y1 = y0;
	GLint a = 0;
	GLint y = (int)(R * 1.0 / (sqrt(2)));
	GLfloat d0 = 1.25 - R;
	GLfloat d;
	glPointSize(pointsize);
	GLint cx = 0, cy = R;
	glVertex2i(0, 0);
	while (a <= y) {
		glVertex2i(x1+a, y1+cy);
		glVertex2i(x1-a, y1-cy);
		glVertex2i(x1-a, y1+cy);
		glVertex2i(x1+a, y1-cy);
		glVertex2i(x1+cy,y1-a);
		glVertex2i(x1-cy,y1-a);
		glVertex2i(x1-cy, y1+a);
		glVertex2i(x1+cy, y1+a);
		a++;
		if (d0 <= 0) {
			d0 = d0 + 2 * a + 3;
			cy = cy;
		}
		else {
			d0 = d0 + 2 * (a - cy) + 5;
			cy = cy - 1;
		}
	}

}

void display()
{
	glClearColor(1.0, 0.8, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.0, 0.0f, 0.0f);
	glBegin(GL_POINTS);
	Bresenham(0,100,100); //调用函数
	glEnd();
	glFlush();
}
void draw_pixel(int ix, int iy)
{
	glBegin(GL_POINTS);
	glVertex2i(ix, iy);
	glEnd();
}

void myinit()
{
	glClearColor(1.0, 0.8, 1.0, 1.0);
	glColor3f(0.0, 0.5, 0.5);
	glPointSize(1.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, 1000.0, 0.0, 1000.0);
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(600, 600);
	glutCreateWindow("Breasenham算法画圆");
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-500.0, 500.0, -500.0, 500.0);
	glutDisplayFunc(display);
	glutMainLoop();
	return 0;
}

运行结果:

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战_第4张图片


2.6 研究体会

  1. 实验环境配置和头文件安装: 通过本次实验,我成功完成了所需的环境配置,并使用EasyX安装了相应的头文件。在Visual Studio 2022开发平台中,我顺利进行了C++编程,这包括了配置开发环境、安装必要的库和头文件等步骤。这为后续的图形学实验提供了一个稳定的基础。

  2. DDA算法和Bresenham算法的实现与比较: 在实验中,我分别实现了DDA算法和Bresenham算法用于生成直线和圆。我对这两种算法的效率和精度有了更深刻的理解。Bresenham算法相较于DDA算法在速度上更快,因为它避免了直线斜率的计算和浮点数运算,只使用整数。然而,DDA算法在精度上更高,因为它使用浮点数运算,但可能不如Bresenham算法快速。了解了它们的特点,我能够在选择算法时更好地权衡速度和精度。

  3. Visual Studio 2022开发平台和函数库的使用: 在实验中,我发现之前可用的getch()函数需要替换为_getch()。通过查询,我了解到带下划线的函数一般是函数库内部的函数,而不带下划线的一般是提供给用户使用的函数。这是为了防止用户定义的函数和函数库的函数重名冲突。这个经验使我更加熟悉了Visual Studio 2022开发平台的使用,并对函数库的命名规范有了更清晰的认识。


总结

图形学领域宛如一片广阔而未被完全探索的创意海洋,邀请你勇敢踏足数字艺术和计算机图形学的神秘领域。这是一场富有创意和技术挑战的学习之旅,从基础概念到算法实现,逐步揭示更深层次的图形分析、渲染技术和智能图形识别的奥秘。渴望挑战图形学的学习路径和掌握计算机艺术的技能?不妨点击下方链接,一同探讨更多数字创意的奇迹吧。我们推出了引领趋势的 计算机图形学专栏:《艺术之光 | 数字创新解锁》,旨在深度探索图形学技术的实际应用和创新。

你可能感兴趣的:(#,【图形学】,DDA,Bresenham,图形学,算法,C语言,C++)