计算机图形学是算比较抽象的一门课程吧,而且内容也比较枯燥,如果没有比较好的耐心,一时半会儿是看不见计算机图形学究竟有什么作用的,但是其中有些内容呢跟C语言有关联,比如直线的扫描转换算法。大部分时候C语言常常以控制台面板、文字、字母示人,但是计算机图形学中的小程序就常常以图形表现了。
直线的三种扫描转换算法:
- 数值微分算法(Digital Differential Analyzer,简称DDA)
- 中点画线法
- Bresenham算法
注意:我使用的是VC++ 6.0,但是我们在程序中添加的graphics.h是没有包含在库当中的,所以我们要手动添加这个头文件。否则是没有办法显示图形的。大家可以在网络上搜索到这个文件的安装方式,操作很简单。
数值微分算法(DDA)
数值微分法是最基础的直线扫描转换算法,所以需要掌握。
这个算法要求先算出直线的斜率,然后从起点开始,确定最佳逼近于直线的y坐标。假设起点的坐标为整数,直线方程为y=kx+b,k的取值在0到1之间,x每递增1,y相应地递增k。
“数值微分算法的本质,是用数值方法解微分方程,通过同时对x和y各增加一个小增量,计算下一步的x、y值。在一个迭代算法中,如果每一步的x、y值是用前一步的值加上一个增量来获得的,那么,这种算法就称为增量算法。因此,DDA算法是一个增量算法。“
说白了,我们计算每一个点不是代入坐标值通过直线方程”硬算“的,而是我们明确了直线有一个规律x每递增1,y相应地递增k,每次计算点的坐标,我只需要x+1,y+k就行了,避免了乘除,而只有加减。
但是这里有一个问题,实际的直线并不是和像素点完全重合的,只能取离实际点最近的那个像素点。这里就有了一个判别的过程,但是下面的程序我认为很巧妙的一个地方就是它利用了C/C++的一个特点,如代码行(1),C/C++对小数的取整不是小学学习的四舍五入,而是直接把小数部分全部去掉,无论是0.1还是0.9。利用这个特性,在实际点的y坐标上加上0.5,然后再取整,就能够实现”四舍五入“。
/***************************************************
*Authors :Philip
*Email :cf20090901@outlook.com
*Last modified :2016-11-10 18:34
*Filename :DDALine.cpp
*Desciption :用数值微分法画一条直线
*
*
***************************************************
*/
#include<iostream.h>
#include<graphics.h>
#include<conio.h>
//using namespace std;
void DDALine(int x0,int y0,int x1,int y1,int color)
{
int x;
float dx,dy,y,k;
dx=x1-x0;
dy=y1-y0;
k=dy/dx;
y=y0;
for(x=x0;x<=x1;x++)
{
putpixel(x,int(y+0.5),color);//(1)
y=y+k;
}
}
//===================================
void main()
{
initgraph(400,400);
DDALine(0,0,400,400,GREEN);
getch();
closegraph();
cout<<"OK"<<endl;
}
中点画线法
所有的算法最关键的部分就是怎么取离直线最近的点,实际上就是一个判断的过程。
中点画线算法关键在于它的名字——中点。上下两个像素点连线的中心就是中点,直线在经过像素点的连线的时候形成的交点就是实际的点,只需要判断这个实际点在”中点“的上方还是下方,如果在上方那么取上面的像素点,如果在下方就取下方的像素点。
/***************************************************
*Authors :Philip
*Email :cf20090901@outlook.com
*Last modified :2016-11-10 19:56
*Filename :DDALine.cpp
*Desciption :用中点画线法画一条直线
*
*
***************************************************
*/
#include<iostream.h>
#include<graphics.h>
#include<conio.h>
void MindpointLine(int x0,int y0,int x1,int y1,int color)
{
int a,b,d1,d2,d,x,y;
b=x1-x0;
a=y0-y1;
d=2*a+b;
d1=2*a;
d2=2*(a+b);
x=x0;
y=y0;
putpixel(x,y,color);
while(x<x1)
{
if(d<0)
{x++,y++,d+=d2;}
else
{x++,d+=d1;}
putpixel(x,y,color);
}
}
//===================================
void main()
{
initgraph(800,600);
MindpointLine(10,50,400,400,RED);
getch();
closegraph();
}
Bresenham算法
Bresenham算法”其实“比较像数值微分法,也是增量算法,但是相对来说更利于硬件实现。
这里引入了一个误差项,判断误差项的正负就可以判断如何取点。
/***************************************************
*Authors :Philip
*Email :cf20090901@outlook.com
*Last modified :2016-11-10 14:09
*Filename :IntegerBresenhamLine.cpp
*Desciption :用Bresenham算法画一条直线
*
*
***************************************************
*/
#include<iostream.h>
#include<graphics.h>
#include<conio.h>
void IntegerBresenhamline(int x0,int y0,int x1,int y1,int color)
{
int x,y,dx,dy,e,i;
dx=x1-x0;
dy=y1-y0;
e=-dx;
x=x0;
y=y0;
for(i=0;i<=dx;i++)
{
putpixel(x,y,color);
x++;
e=e+2*dy;
if(e>=0){y++;e=e-2*dx;}
}
}
//===================================
void main()
{
initgraph(800,600);
IntegerBresenhamline(56,56,400,100,RED);
getch();
closegraph();
}
经验不足,水平有限,有错误或者可以优化的地方欢迎大家指证。谢谢。