DongGu
如何画一条"直"线

如何画一条"直"线

如何在计算机上画一条直线

想必我们在小学二年级学函数的时候就已经背得滚瓜烂熟:直线公式 。但在屏幕上显示一条直线,却不能直接套用这个数学公式——因为数学世界和屏幕世界有着本质的不同。

从”连续”到”离散”

数学上的直线由无数个没有大小的点组成,拥有无限的分辨率,是连续的。但显示器本质上是一个由像素(Pixel)组成的巨大网格,是离散的——每个像素有固定的位置,要么亮,要么不亮,不存在”半个像素”。

连续到离散:光栅化示意图

如上图所示,我们在计算机上只能”逼近”一条直线,画出一个个离理想直线最近的像素点。所幸现代显示器的分辨率足够高,肉眼几乎看不出锯齿。

光栅化(Rasterization):将矢量图形(点、线、面)转换为像素点阵的过程,是计算机图形学中最基础的操作之一。无论是游戏中的 3D 场景还是 GUI 界面上的一个按钮,最终都要经过光栅化才能显示在屏幕上。


直线算法

问题定义:给定直线两端点 ,在屏幕上画出该直线。

下面介绍两种经典算法:DDA 和 Bresenham。其中 Bresenham 算法尤其重要——几乎所有规则图形的绘制都离不开它的思想。


DDA 算法(Digital Differential Analyzer)

DDA 算法的核心思想很朴素:从起点出发,每一步沿直线前进一个微小增量,四舍五入取最近的像素点。

推导

设直线斜率为

根据斜率大小,选择一个方向作为”主方向”,每次前进 1 个单位,另一方向前进 个单位:

  • 方向变化更快):以 为主方向。每次 前进 1, 前进

  • 方向变化更快):以 为主方向。每次 前进 1, 前进

由于像素坐标必须是整数,每步计算出的 需要四舍五入后再绘制。

算法步骤

为例:

  1. 计算 ,初始点
  2. 绘制
  3. 更新
  4. 重复步骤 2-3,直到

下面这张图直观展示了 DDA 的执行过程:

DDA算法执行过程示例

DDA 算法的特点

优点 缺点
思想直观,易于理解和实现 每步都需要浮点数加法,速度较慢
增量计算,无需每次重新求值 round() 取整也是耗时操作
适用于任意斜率的直线 浮点累加会产生误差积累
不利于硬件实现——浮点运算单元远比整数运算单元复杂

Bresenham 算法

DDA 的致命弱点是依赖浮点数运算。1962 年,IBM 的 Jack Bresenham 提出了一种仅用整数运算的直线算法,至今仍是图形学中最优雅的算法之一。

直线的一般方程

将直线方程 改写为隐式形式。代入

两边乘 并移项:

对于直线上的点,。对于不在直线上的点:

  • 直线上方的点:
  • 直线下方的点:

平面被这条直线分成了三个区域:

Bresenham:F(x,y)区域划分

的符号就像一个指南针,告诉我们当前点在直线的哪一侧。

核心思想:中点决策

以下推导假设 (即 ),其他情况可以通过对称变换归约。

假设当前已画出像素点 ,下一个要画的点是 这一列上的哪个像素?由于 最多变化 1,所以只有两个候选:

  • E(East)——保持 不变
  • NE(Northeast)—— 加 1

Bresenham:中点决策示意

如何选择?Bresenham 的巧妙之处在于:考察两个候选点之间的中点 ,判断它在直线的上方还是下方:

  • 在直线上方),说明理想直线从 下方穿过 → 更近 → 选 E
  • 在直线下方),说明理想直线从 上方穿过 → 更近 → 选 NE

代入 ,得到判别式

递推公式

直接每一步都重新计算 太浪费了,我们可以利用增量关系递推。

  • 当选 E 时:下一个中点为

  • 当选 NE 时:下一个中点为

初始值

第一个中点取

至此,算法每步只需要比较 的符号,然后做一次整数加法。但 中还有 这个浮点数——所以我们把所有 值乘以 2,符号判断不受影响:


这样一来,整个算法中只涉及整数加减法和位左移(乘 2),没有任何浮点数运算!

算法步骤(

  1. 输入端点
  2. 计算
  3. 绘制点
  4. ;若 ,则 ;否则
  5. ,重复步骤 3-4

推广到任意斜率

上面只讨论了第一象限 的情况。对于其他七种情况,可以利用对称性:

象限 斜率范围 处理方式
第一象限 原算法,x 为主方向,E/NE 决策
第一象限 交换 x、y 角色,y 为主方向
第二象限 x 递减,交换 x、y
第二象限 x 递减,y 可能递减
按符号对称处理

实现时可以根据 的符号和大小关系,归约到统一框架。这也是 Bresenham 算法的强大之处——同样的思想适用于所有八分圆(octant)。


DDA vs Bresenham 对比

DDA Bresenham
运算类型 浮点数加法和舍入 仅整数加减法
每步计算量 1 次浮点加 + 1 次取整 1 次比较 + 1 次整数加
硬件友好度 低(需要 FPU) 高(仅需 ALU)
精度 浮点累加可能漂移 精确,无累积误差
直观性 非常直观 需要一点推导
适用范围 仅直线 思想可推广到圆、椭圆等

下一篇我们还是会围绕Bresenham算法展开,简单讲解一下讲解圆和椭圆的绘制。


这次的插图来自画师luoyu

图片地址:https://www.pixiv.net/artworks/116826824

本文作者:DongGu
本文链接:https://donggu.xyz/2026/05/11/图形学入门/如何画一条“直”线/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可