[未完结]数字微分分析法的直线绘制(DDA)

注意!
本文被第1次更新,可能存在后续更新

直线画法

直线的斜截式方程

在二维空间下,一条直线的方程可以被描述为若干种形式,其中比较常见的一种是斜截式方程:

y=kx+b

其中k称为直线的斜率,反映直线的倾斜情况,假定直线与水平面成角θ(右侧角),则kθ存在如下关系:

k=tanθ

b称为截距,反映该直线在二维空间中的位置

数字微分分析(DDA)

可对直线y=kx+b进行微分处理得到:

dydx=k=tanθ

对于缓直线|k|1)的时候,可考虑将直线考虑为X型直线(水平走向),这个时候可取x方向的单位点作为取样点计算y的值:

y=kx+b(xZ)

对于给定的线段(x1,y1)(x2,y2),这里考虑取样值的走向δx为:

δx=sgn(x2x1)

这确定了,若曲线走向从右向左的时候,这条直线也是以这个走向被绘制出来而不至于南辕北辙。

缓直线之所以考虑成X走向,是因为对于每一个x+δx,在δx=1的时候,其得到的δy总小于1,而对应地,对于陡直线|k|1)则相应地令δy=1,则对应于y+δy得到的δx总小于1,这样也就把陡曲线考虑为Y向曲线得到的误差是在可接受范围之内的(当差值δ>1的时候,轴向上相邻两点将得到两个不邻接的像素点亮,这将打破直线的连续性并使得直线观感变差)。

若结果为小数,则遵循四舍五入原则(加0.5后再取整)

也可考虑银行家舍入法(Banker 's Rounding)进行舍入

极端情形

完全水平(k=0)

这种情况下,线段认为X型这种情况下y只能得到b

完全垂直(k=∞)

这种情况下直线不存在斜截式方程、斜率式方程(因为斜率和截距都不存在),斜率被认为无穷大(正无穷或负无穷),这种情况下显然被认为Y型曲线

方向通用情形

一般地,如果绘制一条线段,我们更多的会考虑这种绘制方式:

给定两点A和B,然后通过两点可以确定一条直线以及这条线段。

其中,给定两点A和B即给定这两点的坐标,这两种坐标可以被表示为:

PA(xa,ya),PB(xb,yb)

这里没有体现出一个至关重要的信息:斜率,但这一信息可以通过这两个坐标推算得出,由于给定两点有先后顺序(这个顺序将决定绘制的走向),因此这两点可认为构成了向量

v=PBPA=(xbxa,ybya)=(Δx,Δy)

如果考虑把这个向量投射到复平面上,可以得到:

z=Δx+iΔy

而斜率k的反正切即为z辐角

argz=arctanΔyΔx=arctank

但需注意,这里Δx可能为0,同样地,Δy也有类似的可能,当仅有Δy=0的时候,有k=0,然而,其他的情况要复杂得多,若仅有Δx=0,则直线是铅直的,也就是斜率为∞,而两个同时为0的时候则上式直接变为未定式(此时两点重合,实际上得到的是0,而这个向量是任意方向的)。
但所幸,一般的程序数学库中提供了函数atan2可以很好的解决这个问题,atan2的提出主要是出于两种目的:

  1. 解决斜率为∞的情形
  2. 降低当x或y中任意一个值过大而导致的运算误差(这实际上无法规避)

atan2取得两个参数xy作为输入并返回这个向量正确的方位角θ(当然,实际上是弧度形式)。尽管它并不得到斜率(因为其本质还是反正切,正好把θ从正切中取出),但是直接通过方位角,我们仍然可以确定一些事情:

|k|>1±π4<θ<±3π4

|k|<1π4<θ<π43π4<θ<5π4(3π4)

这里还需考虑,当xy都为0的时候,这可能导致atan2无法正常工作,因此这个情况需要预先代替atan2进行处理。

光栅通用情形(注意这里着重讨论端点不落在整数位上)

由于在屏幕上是离散空间,因此绘制需要光栅化,因此,当结果存在小数时,则应当进行适当的舍入操作,当然,上文提及一种策略是四舍五入策略。

但是这仅仅是对于中间确定点亮像素的情形,这里仍然需要考虑:如果PAPB的坐标就很不老实该怎么办??

如果此时提前对PAPB做光栅化(坐标取整)则是否会存在潜在的问题??

这里将这两点的坐标做如下考虑:

xa=Ixa+Fxa

xb=Ixb+Fxb

IZ,|F|<1

以人话的方式说,这里面I表示整数部分,F表示小数部分。
这里只考虑有斜率的情形,斜率可以表述为:

k=(Iyb+Fyb)(Iya+Fya)(Ixb+Fxb)(Ixa+Fxa)

假设Fyb>0.5,Fya<0.5,Fxb<0.5,Fxa>0.5,则

k=(Iyb+1)IyaIxb(Ixa+1)

此时造成误差:

Δk=kk=2af+ag+aha+2bebgbhbce+cf+cde+df+def+g+he2egehef2+fg+fhf+g+h

a=Iyb,b=Fybk=a+bcde+fgh

上式计算过于复杂,这里定性分析一下,考虑到小数部分都很小,因此两个F相乘的项忽略不计,I乘以F的项统一认为是同一阶,最后剩下的部分是:

kagabce+c+def+g+he2egef+g+h

其中认为所有I乘以I也是同阶的。所有的I和所有的F分别内部彼此同阶,则上式再次化简得到:

kΔIΔF0

好吧我承认这是一个非常敷衍了事的证明方法(如果你看着不爽,你完全可以认为这™就是一团废话然后自己证明一番,但我会认为你十有八九会面对一堆abcd无从下手,当然也许我方向就有问题,你完全可以认为这个分析过程是错的),因为这里把很多东西混淆掉了并且忽略了众多的情况,这是一个很粗糙的分析,但是这个提示我们一个信号:分子阶次高于分母。

这提示了一个问题,当两个点离得非常远的时候如果提前把两个点光栅化的话,斜率可能会发生较为显著的变化(当然,真正拿图画出来的时候可能更直观,但老实说这个误差从某种程度上来说差角确实不算大,但斜率确实是有明显的变化),当斜率发生变化的时候最主要导致的问题就是

我™又得重新确定点了!!因为线偏了!!

当然,更糟糕的是,当某一点沿着线段移动到另一点的时候,你将看到这个线段会鬼畜地抖动(因为每次运动一段距离之后可能变成整数点也可能不会变但误差形式可能又会发生变化然后线的偏离程度可能也会发生变化),当然这个幅度并不会很大,但是问题在于如果线段越长这种效应恐怕是越明显的,最重要的是,无论幅度大小与否这根本就不应当发生,但是,我们仍然有方法去解决这个问题,其实很简单,在对端点进行光栅化之前,先把斜率求出来。

对线影响最强烈的莫过于斜率!!!
线越长,影响越明显

方向对于一条直线来说太重要了,考虑一下当你正拿着一把楞次(会爆炸的弓箭)瞄准一个正在蹲坑的Grineer,然后在发射的时候你的手抖那么1度,命中了在隔壁扫描结合目标的队友,他在目标的10米以外的位置……
确定了方向,就意味着这条直线不会走向一个很奇怪的位置,在这个算法中才不至于产生这种效应。

但如果仅仅是这样的话,这条线存在可能比预期低那么1个像素的可能,当然这也是非常不明显的,而且实际上也只有几个点会发生这种变化,这个实际上也不会造成什么抖动的效应,不过本着力求完美的原则,而且这个过程并不会浪费太多的时间,这里还是考虑把点多少修正一下,这个做法也同样很简单,那就是并非直接对端点进行舍入,而是顺着端点推到最近的整数位点计算它对应的y值。

To be continued...

posted @   Oberon  阅读(621)  评论(0编辑  收藏  举报
编辑推荐:
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
点击右上角即可分享
微信分享提示