前言?

如题,”直线直吗“?当思考这个问题时,我们回过头想想之前的文章所讲的直线是由什么组成的:WebGL 中图元有点、线和三角形,虽然三者都被定义为最基础的图形,但它们之间也有着密不可分的联系,正如《酒干倘卖无》中唱到的:”没有点哪有线“ 直线段便是由许多像素点组成的图形。

我们知道像素点其实是正方形的,那么如果展示的直线是水平或者垂直于水平方向的,那么像素点的排列也必然是沿水平或垂直于水平方向的,那么此时直线必然是直的。以俄罗斯方块距离,此时的直线应该是对应俄罗斯方块中的这个图形:

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

那这些像素点是以何规则排布的呢?就此引出我们今天的主题——直线段的扫描转换算法?

直线段的扫描转换算法?

在之前将 WebGL 工作流程时我们提到过一个环节叫做 光栅化,比较标准地解释是确定最佳逼近图形的像素集合,并用指定属性写像素的过程称为光栅化或图形的扫描转换

听懂没?没?说白了就是把图像转化为像素点的过程。

本篇主要介绍三种直线段光栅化的算法:数值微分法、中点画线法和 Bresenham算法

数值微分法

数值微分法(DDA) 也称为 离散插值分析法 (本文统称为数值分析法)。

大家都知道在坐标系中可以用 来表示一条直线,那么当我们知道了我们要绘制线段的端点 , 后,即可通过 两点式 求出其所在直线的表达式:,从而再转换成 的形式。, 分别就是点在屏幕上的坐标,假设 从 开始,每次绘制向 端点移动距离为1像素,那么我们要求的下一个点 :

这样我们就把求要绘制点的算法简化成了一个加法。同时,对屏幕而言,像素点的坐标值并没有小数一说所以需要对所求得的 值进行四舍五入处理。代码如下:

// vertex shaderattribute vec4 a_Position;void main() {  // 为了更明显,特将点的大小设置为10px  gl_PointSize = 10.0;  gl_Position = a_Position;}// fragment shadervoid main() {  gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);}
let current = 0; // 当前顶点索引let handle = -1;let a_Position;const POINTS = []; // 要绘制的顶点数组const P0 = [-300, -200]; // 起始端点const P1 = [500, 200]; // 终止端点const k = (P1[1] - P0[1]) / (P1[0] - P0[0]); // 斜率

// 主函数// 与之前的基础文章内容大致相同,不再赘述function main() {  const canvas = document.querySelector('#canvas');  const gl = canvas.getContext('webgl');

  initShaders(gl, VS, FS);

  a_Position = gl.getAttribLocation(gl.program, 'a_Position');

  gl.clearColor(0.0, 0.0, 0.0, 1.0);  gl.clear(gl.COLOR_BUFFER_BIT);

  animation(gl);}

function animation(gl) {  if (current === P1[0] - P0[0]) { // 退出animation    cancelAnimationFrame(handle);    return;  }  DDA(gl);  handle = requestAnimationFrame(() => animation(gl));}

// DDA算法function DDA(gl) {  // 将下一个要绘制的点存入顶点数组,将px转为webgl系统使用的[-1.0, 1.0]  POINTS.push([    (current+P0[0])/1200, // x    (Math.round(current*k+P0[1]))/800, // y  ]);  current++;

  gl.clear(gl.COLOR_BUFFER_BIT);

  // 循环绘制  for(let i = 0; i     gl.vertexAttrib3f(a_Position, POINTS[i][0], POINTS[i][1], 0.0);    gl.drawArrays(gl.POINTS, 0, 1);  }}

效果如图:

注意,该算法仅适用于 的情况,当 时,需要将 , 互换,即 增加1, 对应增加 。

中点画线法

假如当前绘制的点是 ,则当在绘制下一个点时我们实际上有两种选择: 和 。若 为 , 的中点, 为理想直线与直线 的交点。则当 在 下方时, 应为下一个像素点;否则, 应为下一个像素点。这就是 中点画线法 的原理,如下图:

我们将直线方程标准化后可以得到: 的形式,其中 ,同时点与直线的有如下关系:

因此,只需将点 带入 即可判断中点 与直线的关系。后续绘制就没什么两样了。

Bresenham 算法

该算法与中点画线法类似,只不过在此算法中定义了一个 误差项 , 并不是一个固定值,而是随着顶点的改变而变化的。误差项初始值为0, 下标每增加1, 的值相应增加直线的斜率值 ,即 。而要绘制顶点的位置与误差项 关系如下:

  • 时,直线与 相交的点最接近于当前像素 的右上方像素 ;
  • 时,交点更接近于当前像素右侧的像素 ;

注意,假如下一个要绘制的点为右上方的像素时,因为我们选取了新的 方向的基准点,故 要减1。

结束语?

总结一下三种直线段扫描算法的原理:

  1. 数值微分法:使用 ,求 ,并四舍五入得到下一个要绘制的像素;
  2. 中点画线法:如果中点在理想直线与 交点的上方,则绘制右侧像素,反之绘制右上方像素;
  3. Bresenham 算法:如果误差项 ,则绘制右上方像素,反之绘制右侧像素。

这就是计算机如何绘制的直线,简简单单,不知和你想象中是否一致?我们在反过头看文章开篇的问题 “直线是直的吗”,或许你自己心中也有了答案!?

大家可以看到上面图片中的直线有锯齿,这是因为像素逼近有误差,会使图形发生畸变,这种现象称为 走样。当然也有解决这种问题的技术方法,而这些技术则就称为 反走样,比如最粗暴的方法就是提高分辨率,同时还有 区域采样、加权区域采样 等方式。感兴趣的小伙伴可以自行了解?

感谢阅读!

d3.js中点可以用图片吗_WebGL 直线直吗?相关推荐

  1. d3.js中点可以用图片吗_结论第16课——抛物线的中点弦斜率

    秒杀结论: 中点弦斜率="左右 除 ,上下 除 " 注意要加上抛物线开口方向所确定的符号: 开口向右和向上的,符号为正 开口向左和向下的,符号为负 原理: 例.点 是抛物线 上两点 ...

  2. d3.js html显示图片,d3.js v4:如何在鼠标点击节点后显示图像

    在使用d3.js时仍然相当缺乏经验,我碰到了一个障碍. 希望有人能帮助我.d3.js v4:如何在鼠标点击节点后显示图像 我试图在鼠标单击图形中的节点时显示图片. 理想情况下,我想单击几个节点并显示图 ...

  3. 【项目技术点总结之一】vue集成d3.js利用svg加载图片实现缩放拖拽功能

    [项目技术点总结之一]vue集成d3.js利用svg加载图片实现缩放拖拽功能 前言 概述 技术介绍 实现过程 插件安装 引用组件 初始化组件 实现效果 简单理解 使用d3创建一个svg 在svg中提添 ...

  4. d3.js画柱状图超详细教程

    d3.js画柱状图超详细教程 完整代码下载链接:https://download.csdn.net/download/qq5q13638/85248934,直接用这个文件夹内打开即可. 下面是完整教程 ...

  5. AMap + echarts、google map + d3.js分别实现数据可视化中的飞线图(迁徙图)

    首先肯定是给出demo啦: 演示demo 直接到左侧选择框中选择View taxi flow里面随便选个日期 总体介绍 最近由于工作室项目需要做一个数据可视化平台,这个平台最终是交由国外人使用的.而国 ...

  6. D3.js 教程: 使用 JavaScript 创建可交互的柱状图

    原文链接:D3.js Tutorial: Building Interactive Bar Charts with JavaScript 译者:OFED 最近,我们有幸参与了一个机器学习项目,该项目涉 ...

  7. js实现移动端图片预览:手势缩放, 手势拖动,双击放大...

    原文:js实现移动端图片预览:手势缩放, 手势拖动,双击放大... 前言 本文将介绍如何通过js实现移动端图片预览,包括图片的 预览模式,手势缩放,手势拖动,双击放大等基本功能: 扫码查看示例效果: ...

  8. d3.js实现隐藏部分关系

    如果用d3画人物图的话,这个问题会经常遇到. 例如:人物A和人物B之间的关系有:同学.亲戚.同事:人物A和人物C之间的关系有:同学.同事: 需求:不想看到亲戚关系.(为什么会有这样的需求:节点多了后, ...

  9. d3.js 制作简单的贪吃蛇

    d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的.今天我们使用d3.js配合es6的类来制作一个童年小游戏–贪吃蛇.话不多说先上图片. 1. js snaker类 class Sna ...

  10. d3.js 制作简单的俄罗斯方块

    d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的.今天我们使用d3.js配合es6的类来制作一个童年小游戏--俄罗斯方块.话不多说先上图片. 1. js tetris类 由于方法拆分 ...

最新文章

  1. 腾讯游戏分享汇:天天飞车六大研发经验
  2. MDK 工程宏定义的应用
  3. CCS5连接调试C64X系列DSP核
  4. linux性能并发 带机量,性能测试笔记(一):吞吐量与并发数
  5. 3D数学读书笔记——矩阵进阶
  6. 【vim】几种模式的切换
  7. .NET Core开发日志——RequestDelegate
  8. 第十节: EF的三种追踪实体状态变化方式(DBEntityEntry、ChangeTracker、Local)
  9. CANE:上下文相关动态图网络表示
  10. 学计算机应用技术应具备什么素养,2018年云南经济管理学院单招计算机应用技术职业适应性测试大纲...
  11. 中国象棋ai人工智能(网页版)
  12. 计算机控制plc开机,PLC控制系统与工控计算机控制系统的区别
  13. python掷骰子实验代码_Python Tkinter实例——模拟掷骰子
  14. vivo Y85的usb调试模式在哪里,打开vivo Y85usb调试模式的方法
  15. 面试技巧、专面、HR面、群面
  16. git pull报错:Your configuration specifies to...from the remote, but no such ref was fetched
  17. Revit机电翻模插件【管线编辑】跨系统分类
  18. log4j与slf4j的区别
  19. Cloudflare友情提醒:使用SNI的必要性
  20. 第4代乐视超级电视全系亮相,生态电视“王炸”来了

热门文章

  1. Lync Server 2010迁移至Lync Server 2013部署系列 Part14:A/V服务器目录迁移
  2. C++中static的用法
  3. [转帖] 职场学习=贼学技术
  4. QT发布windows程序
  5. 【狂神说Redis】1NoSQL概述 1-1为什么使用NoSQL
  6. 【问题解决】c.a.c.n.c.NacosPropertySourceBuilder : parse data from Nacos error,dataId:xxxxxx.yml
  7. c语言学生管理p1指向编译错误,在ubuntu下用C语言编写一个学生管理系统,编译时出错,紧急求救!!!...
  8. mybatis的mysql参数传递_Mybatis参数传递及返回类型
  9. 【转】NodeJs使用Mysql模块实现事务处理
  10. 【转】JQuery中$.each 和$(selector).each()的区别详解