【计算机图形学】直线的两种生成算法(DDA算法、Bresenham算法)

2024-03-30 12:58

本文主要是介绍【计算机图形学】直线的两种生成算法(DDA算法、Bresenham算法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

直线的两种生成算法(DDA算法、Bresenham算法)


文章目录

      • 1.计算机绘制直线的原理
      • 2.DDA算法的原理与实现(基于matlab)
      • 3.Bresenham算法的原理与实现(基于matlab)




1.计算机绘制直线的原理

在计算机中,直线的显示并不是连续的,而是离散的点,这是由光栅化的本质决定 的。我们可以把屏幕理解为阴极射线管光栅显示器,这个显示器是由离散可发光的有线 区域单元(像素)组成的矩阵。确定最佳逼近某直线的像素的过程通常叫做光栅化。对于 水平线、垂直线以及 45°线,选择哪些光栅元素是显而易见的,而对于其他方向的直线, 像素的选择就很困难。因此,选择一个重要的算法来实现直线的绘制显得很有必要。




2.DDA算法的原理与实现(基于matlab)

DDA 算法是直线算法里最为简单的算法,它的基本思想是高数里的微分算法:

d y d x = y 2 − y 1 x 2 − x 1 \frac{\mathrm{d} y}{\mathrm{d} x}=\frac{y_2-y_1}{x_2-x_1} dxdy=x2x1y2y1

其有限差分近似解为:

{ x i + 1 = x i + Δ x y i + 1 = y i + y 2 − y 1 x 2 − x 1 Δ x \begin{cases} x_{i+1} = x_i+\varDelta x \\ y_{i+1} = y_i+\frac{y_2-y_1}{x_2-x_1}\varDelta x \end{cases} {xi+1=xi+Δxyi+1=yi+x2x1y2y1Δx

根据这个算法,我们可以将某条线段的两个端点作为输入,并利用这两个端点来将此线段进行微分(迭代求值),在每轮迭代中,将微分得到的每个点单独绘制。最终,所有单独绘制的点就构成了我们所需要绘制的直线。


下面给出用matlab实现该算法的完整代码:

function main					%测试函数
clc
clear;
DDA(0,0,30,40,'r')    			%勾三股四弦五function DDA(x1,y1,x2,y2,color)		%DDA算法dx=(x2-x1);dy=(y2-y1);step=max(abs(dx),abs(dy));deltax=dx/step;deltay=dy/step;x=x1;y=y1;hold onfor i=1:stepscatter(round(x),round(y),'.',color)    	  %绘出当前点x=x+deltax;                            		  %更新xy=y+deltay;                             	  %更新yendplot([x1,x2],[y1,y2])                       	  %直接绘制原线以对比grid minor										  %添加网格hold off

运行后得到的效果如下:
Alt
上图展示的是在给定较短线段时(长度为50个像素),采用DDA算法绘出的直线情形(上图中的红点部分),其中的蓝色线段是这条线段本身(真实线段)
接下来若将该点设置长一些,即将上面的测试代码改为如下:

function main
clc
clear;
DDA(0,0,100,75,'r')	%勾三股四弦五

则此时的成像效果如下:
Alt
上图中的红点即为DDA算法绘制的给定线段,而其中的黄线则为真实线段
可以发现此时点的密集程度大大增加,成像效果相较于上面也就好一些。




3.Bresenham算法的原理与实现(基于matlab)

原理:
这里仅展示最简单的一种情况(直线斜率在区间 ( 0 , 1 ) (0,1) (0,1) 之间的),其他情况可以类似推导。
设当前像素点为 P ( x p , y p ) P(x_p,y_p) P(xp,yp),下一像素点有两种选择 P 1 P_1 P1 P 2 P_2 P2,设 P 1 、 P 2 P_1、P_2 P1P2 的中点为 M ( x p + 1 , y p + 0.5 ) M(x_{p+1}, y_{p+0.5}) M(xp+1,yp+0.5) Q Q Q 为所画直线与直线 x = x p + 1 x=x_{p+1} x=xp+1 交点,如下图所示:
在这里插入图片描述
Q Q Q M M M 上方时,下一像素点取 P 2 P_2 P2;当 Q Q Q M M M 下方时,下一像素点取 P 1 P_1 P1


实现:

  1. 首先设待画直线的起点和终点坐标分别为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1), (x_2,y_2) (x1,y1),(x2,y2),则根据起点和中点坐标可以得到该直线的斜率为 k = y 2 − y 1 x 2 − x 1 k=\frac{y_2-y_1}{x_2-x_1} k=x2x1y2y1。我们知道直线方程可以用 F ( x , y ) = a x + b y + c F(x, y)=ax+by+c F(x,y)=ax+by+c 的形式来表达,此时该直线的斜率 k = − a b k=-\frac{a}{b} k=ba,那也就是说我们可以直接令 a = y 1 − y 2 , b = x 2 − x 1 a=y_1-y_2, b=x_2-x_1 a=y1y2,b=x2x1(只要满足商为 − k -k k 即可)。
    此时 F ( x , y ) = x ( y 1 − y 2 ) + y ( x 2 − x 1 ) + c F(x,y)=x(y_1-y_2)+y(x_2-x_1)+c F(x,y)=x(y1y2)+y(x2x1)+c,我们再随意带一个点进去(比如 ( x 1 , y 1 ) (x_1, y_1) (x1,y1)),便可求得 c = x 1 y 2 − x 2 y 1 c=x_1y_2-x_2y_1 c=x1y2x2y1 ,即得到该直线的方程为:
    { F ( x , y ) = a x + b y + c = 0 a = y 1 − y 2 b = x 2 − x 1 c = x 1 y 2 − x 2 y 1 \begin{cases} F(x,y)=ax+by+c=0 \\ a=y_1-y_2 \\ b=x_2-x_1 \\ c=x_1y_2-x_2y_1 \end{cases} F(x,y)=ax+by+c=0a=y1y2b=x2x1c=x1y2x2y1

  2. 对于某个点 ( x , y ) (x,y) (x,y),我们要知道其与直线方程 F ( x , y ) F(x,y) F(x,y) 存在如下关系:
    { F ( x , y ) = 0 , 在直线上 F ( x , y ) > 0 , 在直线上方 F ( x , y ) < 0 , 在直线下方 \begin{cases} F(x,y)=0,& 在直线上 \\ F(x,y)>0,& 在直线上方 \\ F(x,y)<0,& 在直线下方 \\ \end{cases} F(x,y)=0F(x,y)>0F(x,y)<0在直线上在直线上方在直线下方
    因此,我们可以直接将中点 M ( x p + 1 , y p + 0.5 ) M(x_{p+1},y_{p+0.5}) M(xp+1,yp+0.5) 带入直线方程中:
    d = F ( x p + 1 , y p + 0.5 ) = a x p + 1 + b y p + 0.5 + c d=F(x_{p+1},y_{p+0.5})=ax_{p+1}+by_{p+0.5}+c d=F(xp+1,yp+0.5)=axp+1+byp+0.5+c,我们就可以根据 d d d 的值来确定下一个像素点:
    { 取 P 1 和 P 2 均可(此处取 P 1 ), d = 0 取 P 1 为下一像素点, d > 0 (表示 M 在直线上方) 取 P 2 为下一像素点, d < 0 (表示 M 在直线下方) \begin{cases} 取 P_1 和 P_2 均可(此处取 P_1),& d=0 \\ 取 P_1为下一像素点,& d>0(表示 M 在直线上方) \\ 取 P_2为下一像素点,& d<0(表示 M 在直线下方) \\ \end{cases} P1P2均可(此处取P1),P1为下一像素点,P2为下一像素点,d=0d>0(表示M在直线上方)d<0(表示M在直线下方)
    若设当前像素点为 P ( x p , y p ) P(x_p, y_p) P(xp,yp),我们考虑 d ≥ 0 d\ge0 d0 d < 0 d<0 d<0 两种情况:

  • d ≥ 0 d\ge0 d0,此时 P 1 ( x p + 1 , y p ) P_1(x_{p+1}, y_p) P1(xp+1,yp) 为下一像素点,则再下一像素点为 ( x p + 2 , y p + 0.5 ) (x_{p+2}, y_{p+0.5}) (xp+2,yp+0.5)
    d ′ = F ( x p + 2 , y p + 0.5 ) = a x p + 2 + b y p + 0.5 + c = a + ( a x p + 1 + b y p + 0.5 + c ) = a + d d'=F(x_{p+2}, y_{p+0.5})=ax_{p+2}+by_{p+0.5}+c=a+(ax_{p+1}+by_{p+0.5}+c)=a+d d=F(xp+2,yp+0.5)=axp+2+byp+0.5+c=a+(axp+1+byp+0.5+c)=a+d,此时 d ′ = a + d d'=a+d d=a+d,即增量为 a a a
  • d < 0 d<0 d<0,此时 P 2 ( x p + 1 , y p + 1 ) P_2(x_{p+1}, y_{p+1}) P2(xp+1,yp+1) 为下一像素点,则再下一像素点为 ( x p + 2 , y p + 1.5 ) (x_{p+2}, y_{p+1.5}) (xp+2,yp+1.5)
    d ′ ′ = F ( x p + 2 , y p + 1.5 ) = a x p + 2 + b y p + 1.5 + c = a + b + ( a x p + 1 + b y p + 0.5 + c ) = a + b + d d''=F(x_{p+2}, y_{p+1.5})=ax_{p+2}+by_{p+1.5}+c=a+b+(ax_{p+1}+by_{p+0.5}+c)=a+b+d d′′=F(xp+2,yp+1.5)=axp+2+byp+1.5+c=a+b+(axp+1+byp+0.5+c)=a+b+d,此时 d ′ ′ = a + b + d d''=a+b+d d′′=a+b+d,即增量为 a + b a+b a+b
  1. 注意到 d 0 d_0 d0 的初始化是用 ( x 1 , y 0.5 ) (x_1,y_{0.5}) (x1,y0.5) 来计算的:即 d 0 = a + 0.5 b + c d_0=a+0.5b+c d0=a+0.5b+c,且在后面的迭代中, y y y 始终都含有 0.5。而我们在迭代中判断下一个点纵坐标的取值时仅仅与 d d d 值的正负有关,因此为了摆脱浮点数的出现,我们统一将所有 d i d_i di 的计算都使用 2 d i 2d_i 2di 来代替,因此 d 0 = 2 a + b + 2 c d_0=2a+b+2c d0=2a+b+2c,增量 d ′ = 2 a d'=2a d=2a,增量 d ′ ′ = 2 a + 2 b d''=2a+2b d′′=2a+2b

下面给出用matlab实现该算法的完整代码:

function main         %测试代码
clc
clear;
Bresenham(0,0,50,40,'r')function Bresenham(x1,y1,x2,y2,color)		%Bresnham算法a = (y1-y2);b = (x2-x1);c = x1*y2-x2*y1;d=2*a+b+c;d1=2*a;d2=2*(a+b);x=x1;y=y1;hold onscatter(x,y,'.',color)for i=x:x2if d<0x=x+1;y=y+1;d=d+d2;elsex=x+1;d=d+d1;endscatter(x,y,'.',color)endplot([x1,x2],[y1,y2])                       %绘制原线以对比grid minorhold off

运行后得到的效果如下:
Alt
可以看出,中点算法在 x x x 轴上进行变化时, y y y上的取值严格按照 Bresenham 的原理进行取值。该算法作为数值微分法(DDA)的改进算法,其采用了直线的一般式方程、增量思想、实现整数加法等优化的思路,其成像的主要思想是以贴合直线为中心,使得这样的算法在绘制出的效果上是优于 DDA 算法的。


END


这篇关于【计算机图形学】直线的两种生成算法(DDA算法、Bresenham算法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/861548

相关文章

深入理解Mysql OnlineDDL的算法

《深入理解MysqlOnlineDDL的算法》本文主要介绍了讲解MysqlOnlineDDL的算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录一、Online DDL 是什么?二、Online DDL 的三种主要算法2.1COPY(复制法)

Java8 Collectors.toMap() 的两种用法

《Java8Collectors.toMap()的两种用法》Collectors.toMap():JDK8中提供,用于将Stream流转换为Map,本文给大家介绍Java8Collector... 目录一、简单介绍用法1:根据某一属性,对对象的实例或属性做映射用法2:根据某一属性,对对象集合进行去重二、Du

k8s admin用户生成token方式

《k8sadmin用户生成token方式》用户使用Kubernetes1.28创建admin命名空间并部署,通过ClusterRoleBinding为jenkins用户授权集群级权限,生成并获取其t... 目录k8s admin用户生成token创建一个admin的命名空间查看k8s namespace 的

Vue3 如何通过json配置生成查询表单

《Vue3如何通过json配置生成查询表单》本文给大家介绍Vue3如何通过json配置生成查询表单,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录功能实现背景项目代码案例功能实现背景通过vue3实现后台管理项目一定含有表格功能,通常离不开表单

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Python使用python-pptx自动化操作和生成PPT

《Python使用python-pptx自动化操作和生成PPT》这篇文章主要为大家详细介绍了如何使用python-pptx库实现PPT自动化,并提供实用的代码示例和应用场景,感兴趣的小伙伴可以跟随小编... 目录使用python-pptx操作PPT文档安装python-pptx基础概念创建新的PPT文档查看

在ASP.NET项目中如何使用C#生成二维码

《在ASP.NET项目中如何使用C#生成二维码》二维码(QRCode)已广泛应用于网址分享,支付链接等场景,本文将以ASP.NET为示例,演示如何实现输入文本/URL,生成二维码,在线显示与下载的完整... 目录创建前端页面(Index.cshtml)后端二维码生成逻辑(Index.cshtml.cs)总结