
前言
如题,”直线直吗“?当思考这个问题时,我们回过头想想之前的文章所讲的直线是由什么组成的:WebGL 中图元有点、线和三角形,虽然三者都被定义为最基础的图形,但它们之间也有着密不可分的联系,正如《酒干倘卖无》中唱到的:”没有点哪有线“ 直线段便是由许多像素点组成的图形。
我们知道像素点其实是正方形的,那么如果展示的直线是水平或者垂直于水平方向的,那么像素点的排列也必然是沿水平或垂直于水平方向的,那么此时直线必然是直的。以俄罗斯方块距离,此时的直线应该是对应俄罗斯方块中的这图形:

但大多数情况下直线并不是严格水平或竖直的,那么这种情况下,我们看到的直线更像是下面这些图形的排列组合:

那这些像素点是以何规则排布的呢?就此引出我们今天的主题——直线段的扫描转换算法
直线段的扫描转换算法
在之前将 WebGL 工作流程时我们提到过一个环节叫做 光栅化,比较标准地解释是确定最佳逼近图形的像素集合,并用指定属性写像素的过程称为光栅化或图形的扫描转换。
听懂没?没?说白了就是把图像转化为像素点的过程。
本篇主要介绍三种直线段光栅化的算法:数值微分法、中点画线法和 Bresenham算法。
数值微分法
数值微分法(DDA) 也称为 离散插值分析法 (本文统称为数值分析法)。
大家都知道在坐标系中可以用
这样我们就把求要绘制点的算法简化成了一个加法。同时,对屏幕而言,像素点的坐标值并没有小数一说所以需要对所求得的
// vertex shader
let
效果如图:

注意,该算法仅适用于
中点画线法
假如当前绘制的点是

我们将直线方程标准化后可以得到:
因此,只需将点
Bresenham 算法
该算法与中点画线法类似,只不过在此算法中定义了一个 误差项
-
时,直线与
相交的点最接近于当前像素
的右上方像素
;
-
时,交点更接近于当前像素右侧的像素
;
注意,假如下一个要绘制的点为右上方的像素时,因为我们选取了新的

结束语
总结一下三种直线段扫描算法的原理:
- 数值微分法:使用
,求
,并四舍五入得到下一个要绘制的像素;
- 中点画线法:如果中点在理想直线与
交点的上方,则绘制右侧像素,反之绘制右上方像素;
- Bresenham 算法:如果误差项
,则绘制右上方像素,反之绘制右侧像素。
这就是计算机如何绘制的直线,简简单单,不知和你想象中是否一致?我们在反过头看文章开篇的问题 “直线是直的吗”,或许你自己心中也有了答案!
大家可以看到上面图片中的直线有锯齿,这是因为像素逼近有误差,会使图形发生畸变,这种现象称为 走样。当然也有解决这种问题的技术方法,而这些技术则就称为 反走样,比如最粗暴的方法就是提高分辨率,同时还有 区域采样、加权区域采样 等方式。感兴趣的小伙伴可以自行了解
感谢阅读,欢迎关注公众号:Refactor!