本模块内容绝大部分是在慕课上看中国农业大学网客时的笔记,因此算作转载,在此鸣谢赵明、李振波两位老师,感谢他们录制该门课程供大家学习!

光栅图形学

为适应光栅显示器,需发展一套和他相适应的算法来处理、显示图形。光栅图形学算法大多数属于计算机图形的底层算法,虽然编程过程中我们一般不会亲自去写它,但它包含很多图形学的基本概念和思想,了解这些算法会对更高级、更深层次内容的理解有很大帮助,亦是本模块重要的第一课。

直线段的扫描转换算法

计算机上不像数学,直线的显示是由有限个像素点来拟合直线,因此要使用一些算法来进行图形的显示。

最容易想到的直线绘制算法

首先需要知道这些离散的像素点的坐标

采用斜截式方程表示直线  y=kx+b,已知直线始末两点坐标,可得k和b,然后求x和y。

此时我们令x在x0~x1之间变化,求y的值。(像素坐标都是整数,所以y值要进行取整处理)

这里我们为了将数学上的点扫描成屏幕上的像素点使用了四舍五入法,即x、y各加0.5,然后取整。大概的代码如下:

public class TraditionalMethod extends JFrame{private int x1,x2,y1,y2;private float k,b;public TraditionalMethod( int x1 , int y1  , int x2 , int y2 ){super();setTitle("最易想到的直线绘制算法");setSize(800,600);setVisible(true);this.x1=x1;this.x2=x2;this.y1=y1;this.y2=y2;traditionalDrawLine(x1,y1,x2,y2);}private void traditionalDrawLine( int x1 , int y1  , int x2 , int y2 ){if (x1==x2){for (int i=y1;i<y2;i++){drawPixel(x1,i);}}else {if (x1>x2){       //保证x1对应的点在x2对应点的左边int temp=x1;x1=x2;x2=temp;temp=y1;y1=y2;y2=temp;}k=(y2-y1)/(x2-x1);b=y1-k*x1;for (int i=x1;i<x2;i++){int j=(int)(k*i+b+0.5);drawPixel(i,j);}}}private void drawPixel(int x1,int y1) {//暂时没找到Java中的点绘制方法,虽然可以用drawLine或drawOval等代替但懒得搞了}}

直线是最基本的图形,一幅图往往要画成千上万条直线,因此直线算法的好坏会直接影响图形的质量和显示速度。上面的算法虽然直观,但效率实在感人,且许多细节未考虑。于是考虑是否能将乘法取消掉从而提高效率?答案当然是可以,所以有下面三种经典直线绘制算法之一——DDA( Digital Differential Analyzer,数值微分法)

三种经典直线绘制算法

1.数值微分法(DDA)

介绍DDA之前需要先引进图形学中一个重要的“增量思想”:

在直线中,x值每次运算固定+1,由于x,y的一次线性关系,所以有像y4=y3+k这样的递推关系,就可以取消掉相对加法慢一些的乘法运算,而用加法代替它达到相同效果。

运用了该思想的DDA算法步骤如下:

  1. 计算k值,并与1比较(*)
  2. 四舍五入由x算y
  3. 递推循环

*注意,这并不适合任意斜率的直线,k<1比较适用,但k>1时,光栅点会随k变大而变稀,直线看上去就像“断了”。此时可以算斜率的倒数,让y每次运算固定+1,然后算x。

public class DDADrawLine extends JFrame {private int x1,x2,y1,y2;private float k;public DDADrawLine( int x1 , int y1  , int x2 , int y2 ){super();setTitle("DDA直线绘制算法");setSize(800,600);setVisible(true);this.x1=x1;this.x2=x2;this.y1=y1;this.y2=y2;DDA(x1,y1,x2,y2);}private void DDA( int x1 , int y1  , int x2 , int y2 ){if (x1==x2){for (int i=y1;i<y2;i++){drawPixel(x1,i);}}else if (y1==y2){               //对于特殊斜率无需计算k值for (int i=x1;i<x2;i++){drawPixel(i,y1);}}else {if (x1>x2){                 //令x1在x2左边int temp=x1;x1=x2;x2=temp;temp=y1;y1=y2;y2=temp;}k=(y2-y1)/(x2-x1);if (k<1&&k>-1){int j=y1;for (int i=x1;i<x2;i++){drawPixel(i,j);j+=k;}}else {             //按y步进,因y1和y2大小关系未知所以需要分别进行k=1/k;if (k>0){int i=x1;for (int j=y1;j<y2;j++) {drawPixel(i,j);i+=k;}}else {int i=x1;for (int j=y1;j>y2;j--){drawPixel(i,j);i+=k;}}}}}private void drawPixel(int x1,int y1) {//暂时没找到Java中的点绘制方法,虽然可以用drawLine或drawOval等代替但懒得搞了}}

其实,这样还不是最优,进一步改进的方向如下:

  1. 改进效率:将浮点运算改进为整数运算
  2. 修改直线方程类型-->中点画线法

2.中点画线法

中点画线法使用了直线的一般式方程F(x,y)=0或Ax+By+C=0,其中A=-△y,B=△x,C=-B△x

此时,直线将坐标系分为3部分:直线上方(F>0),直线下方(F<0),直线(F=0)。我们每次在最大位移方向上走一步,在另一个方向上走还是不走取决于中间误差项的判断。比如0≤|k|≤1时,x++后,y++或者y不变要看与中点M的y值大小

判断方法:

将M代入F,(x=xi+1,y=yi+0.5)得到一个值d,看d与0的大小关系就能判断中点在直线的上面还是下面了

中点画线法的计算量:

目前情况,为得到d,需要2个乘法,4个加法

public class MidPoint extends JFrame {private int x1,x2,y1,y2,d,A,B,C;public MidPoint( int x1 , int y1  , int x2 , int y2 ){super();setTitle("Mid Point直线绘制算法");setSize(800,600);setVisible(true);this.x1 = x1;this.x2 = x2;this.y1 = y1;this.y2 = y2;this.A = y2 - y1;this.B = x1 - x2;this.C = x1*(y1 - y2) + y1*(x2 - x1);midPointDrawLine(x1 , y1 , x2 , y2 , A , B , C);      //基于直线的一般方程Ax+By+C=0}private void midPointDrawLine(int x1 , int y1 , int x2 , int y2 , int A , int B , int C){if (abs(A) < abs(B)){           //判断按x还是y步进的依据,此处为按x步进if (x1 > x2){int temp = x1;x1 = x2;x2 = temp;temp = y1;y1 = y2;y2 = temp;}int j = y1;if (y1 < y2){           //判断从x1递增还是从x2递减,递增情况:for (int i = x1 ; i < x2 ; i++){d=2*A*i+B*(2*j+1)+2*C;        //扩大2倍消去浮点数0.5后再与0比较if (d >= 0){j++;drawPixel(i,j);}else {drawPixel(i,j);}}}else {             //递减情况:for (int i = x2 ; i > x1 ; i--){d = 2*A*i + B*(2*j + 1) + 2*C;if (d >= 0){j++;drawPixel(i,j);}else {drawPixel(i,j);}}}}else{                  //按y步进情况if (y1 >y2){int temp = y1;y1 = y2;y2 = temp;temp = x1;x1 = x2;x2 = temp;}int i = x1;if (x1 < x2) {for (int j = y1; j < y2 ; j++){d = 2*A*i + B*(2*j + 1) + 2*C;if (d >= 0){i++;drawPixel(i,j);}else {drawPixel(i,j);}}}else {for (int j = y2 ; j > y1 ; j--){d = 2*A*i + B*(2*j + 1) + 2*C;if (d < 0){i++;drawPixel(i,j);}else {drawPixel(i,j);}}}}}private void drawPixel(int x,int y){//暂时没找到Java中的点绘制方法,虽然可以用drawLine或drawOval等代替但懒得搞了}
}

优化:

类比前面,可以使用增量计算提高效率

推导d的递推公式如下图所示:

d<0时

d>=0时

步骤:

1)计算d的初始值F(x0+1,y=y0+0.5),有d0=Ax0+By0+C+A+0.5B=>d0=A+0.5B

2)d2=d1+A+B,d<0;d2=d1+A,d≥0……此时,效率至少与DDA算法一样高

**可以用2d0=2A+B,解决掉浮点数运算,全部转化为整数加法

该改进较为简单,偷个懒代码省略嘿嘿

其实,即使已经简化到这样,仍然有很多人愿意进一步去改进它。因为这种很基础的算法每次处理图像都会被几千、几万,甚至几百万次地被调用,该算法每一点点的改进都会被n倍地放大。我看过的一个思路是根据“沿x方向和沿y方向描点是有规律的,找出该规律并循环即可连增量加法也不必要算了”。更多对这些算法的优化读者可以自行去中国知网关键字搜索,只需要一点编程基础,一点初中数学基础和一点点线性代数基础,然后会熟练使用搜索引擎就能看懂绝大多数文章。

图形学(2)光栅图形学的直线绘制(上)相关推荐

  1. 图形学(3)光栅图形学的直线绘制(下)

    本模块内容绝大部分是在慕课上看中国农业大学网客时的笔记,因此算作转载,在此鸣谢赵明.李振波两位老师,感谢他们录制该门课程供大家学习! Bresenham算法 前两种算法把效率提高到了整数加法级别,只讲 ...

  2. 光栅图形学-画直线经典算法

    光栅图形学算法-画直线算法 如果对这些算法感兴趣,可以去查阅算法的详细介绍,这里只是用伪代码来描述这些算法. 1.DDA算法 DDA算法依赖于直线的斜截式方程 n=x2 - x1; //像素点个数k ...

  3. line划线计算机图像学,【计算机图形学】根本图形元素:直线的生成算法

    [计算机图形学]基本图形元素:直线的生成算法 08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活.此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net ...

  4. 图形学日记(一)光栅图形学

    本文主要是概念的总结,并不会讲述具体的算法,如需查看详细内容,请点击相关博客. 1.基础概念: 光栅化(图形的扫描转换):确定最佳逼近图形的像素集合,并用指定属性写像素的过程. 一维图形的表示:在不考 ...

  5. 计算机图形学 第四章 光栅图形学

    第四章 光栅图形学 计算机图形学 第四章 光栅图形学的相关内容,包括:直线段的扫描转换算法.圆弧的扫描转换算法.多边形区域填充.字符的生成.裁剪.反走样 等 Def 光栅显示器:一个像素矩阵(因此,要 ...

  6. 光栅图形学算法基础其二 (裁剪算法)

    光栅图形学算法的研究内容      直线段的扫描转换算法 多边形的扫描转换与区域填充算法 直线裁剪算法 反走样算法 消隐算法 一.裁剪简述         使用计算机处理图形信息时,计算机内部存储的图 ...

  7. 计算机图形学算法【1】——直线画法(数值微分法)

    计算机图形学算法[1]--直线画法(数值微分法) 以下所有代码均在VS2010下的MFC框架下使用 DDA(数值微分法)介绍 一.基本原理 通过计算直线的斜率k和截距b,y = kx+b 1.计算起点 ...

  8. 计算机图形学 实验7 《复杂图形绘制-Bezier曲线与Hermite曲线》

    计算机图形学 实验7 <复杂图形绘制-Bezier曲线与Hermite曲线> 一.实验目的 学习样条曲线的绘制. 二.实验内容 1.绘制Bezier曲线: 2.绘制Hermite曲线. 三 ...

  9. 计算机图形学 实验8 《复杂图形绘制-Bezier曲面及其纹理》

    计算机图形学 实验8 <复杂图形绘制-Bezier曲面及其纹理> 一.实验目的 学习样条曲面绘制技术. 二.实验内容 1.绘制Bezier曲面: 2.将纹理图像映射到曲面. 三.实验方法 ...

最新文章

  1. Scrum敏捷看板工具leangoo-销售流程管理
  2. centos cp 详解
  3. 洛谷P2221 [HAOI2012]高速公路(线段树+概率期望)
  4. 打印 Python 的一切 —— pprint beeprint
  5. python教程:利用while求100内的整数和
  6. window下安装nvm、node.js、npm的步骤
  7. 信道分类、信道复用技术、CSMA/CD 协议、PPP 协议、MAC 地址、局域网、以太网、交换机、虚拟局域网
  8. StringBuilder的构造方法和append方法
  9. Arcgis for javascript不同的状态下自定义鼠标样式
  10. linux kdb内核调试器,使用KDB调试工具
  11. 为什么程序员的社会地位不高?
  12. 回归分析beta值的标准_读懂回归分析-SPSS为例(无广告)
  13. windows下putty 连接linux远程管理
  14. iPhone公司为了节约成本,都干过什么事情?
  15. mysql nest loop伪代码_Nested Loop Join
  16. 解决Android Studio安装过程中“SDK tools directory is missing”的问题
  17. Java将双精度转换为字符串
  18. Ubuntu,安装最新版的GCC
  19. Hypermesh-优化案例学习笔记-cclip
  20. cannot be applied to

热门文章

  1. 手机内置传感器和定位技术
  2. java 外文翻译_计算机 java 外文翻译 外文文献 英文文献
  3. 诺奖以上,真相未满:追捕黑洞二百年
  4. python制作手机壁纸_用Python生成自己独一无二的手机壁纸
  5. neat download manager是什么?多线程下载工具 NeatDownloadManager
  6. opencv python gpu加速_OpenCV中配置CUDA,实现GPU加速
  7. 如何利用实验室信息管理系统LIMS轻松通过CNAS评审?
  8. 第8章第14节:制作企业宣传册的公司团队第一页面 [PowerPoint精美幻灯片实战教程]
  9. 2019计算机考研大纲考什么,2019计算机考研大纲解析
  10. JavaScript Light BOX相册预览功能