当前位置: 代码网 > 科技>操作系统>Windows > 【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战

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

2024年08月01日 Windows 我要评论
博文“探秘图形学奥秘:DDA与Bresenham算法的解密与实战”深入剖析了图形学中的两种重要算法:DDA(数字差分分析)和Bresenham算法。文章首先介绍了这两种算法的基本原理,阐述了它们在图形学中的关键作用。DDA算法以简洁的数学方式描述直线的绘制,而Bresenham算法则以更为高效的方式处理直线绘制过程。

⏰诗赋清音:云生高巅梦远游, 星光点缀碧海愁。 山川深邃情难晤, 剑气凌云志自修。

目录

🌌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. 初识模式识别


🌌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算法思想如下:


🌕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算法画圆生成算法思路如下:


🌍2.5 研究步骤

(1) 在microsoft visual studio 2022环境下创建名为bmpread的mfc应用程序工程(单文档)

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


🌕2.5.1 dda算法代码实现画直线
#include <gl\glut.h>
#include <math.h>
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <easyx.h>

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();
}

运行结果:


🌕2.5.2 breasenham 算法实现画直线

#include <gl\glut.h>
#include <math.h>
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <easyx.h>

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();
}

运行结果:


🌕2.5.3 dda算法代码实现画圆
#include<iostream>
#include<graphics.h>
#include<conio.h>
#include<time.h>
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
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();
}


运行结果:


🌕2.5.4 breasenham算法代码实现画圆
#include <gl/glut.h> 
#include<math.h>
#include<iostream>
#include <easyx.h>
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;
}

运行结果:


🌍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开发平台的使用,并对函数库的命名规范有了更清晰的认识。


📝总结

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com