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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];