【计算机图形学】直线的两种生成算法(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

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

Python使用qrcode库实现生成二维码的操作指南

《Python使用qrcode库实现生成二维码的操作指南》二维码是一种广泛使用的二维条码,因其高效的数据存储能力和易于扫描的特点,广泛应用于支付、身份验证、营销推广等领域,Pythonqrcode库是... 目录一、安装 python qrcode 库二、基本使用方法1. 生成简单二维码2. 生成带 Log

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

Python使用Pandas库将Excel数据叠加生成新DataFrame的操作指南

《Python使用Pandas库将Excel数据叠加生成新DataFrame的操作指南》在日常数据处理工作中,我们经常需要将不同Excel文档中的数据整合到一个新的DataFrame中,以便进行进一步... 目录一、准备工作二、读取Excel文件三、数据叠加四、处理重复数据(可选)五、保存新DataFram

SpringBoot生成和操作PDF的代码详解

《SpringBoot生成和操作PDF的代码详解》本文主要介绍了在SpringBoot项目下,通过代码和操作步骤,详细的介绍了如何操作PDF,希望可以帮助到准备通过JAVA操作PDF的你,项目框架用的... 目录本文简介PDF文件简介代码实现PDF操作基于PDF模板生成,并下载完全基于代码生成,并保存合并P

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

详解Java中如何使用JFreeChart生成甘特图

《详解Java中如何使用JFreeChart生成甘特图》甘特图是一种流行的项目管理工具,用于显示项目的进度和任务分配,在Java开发中,JFreeChart是一个强大的开源图表库,能够生成各种类型的图... 目录引言一、JFreeChart简介二、准备工作三、创建甘特图1. 定义数据集2. 创建甘特图3.