点在计算机中是组成图形的最基本元素,我们几何基本图形的绘制是一个个像素点按照一定规则排列而成的组成的。而复杂的图形是有基本几何图形组成的。所以基本图形的绘制算法,是学习计算机图形学的基础和关键。基本图形包括:直线,矩形,三角形
直线是图形中最常见的,在解析几何中,二维坐标系中的直线的表达式是:y=k*x+b.
由于显示器显示图形是像素点组成的,X轴坐标和Y轴坐标只能用整数表示。所以,不能可能每个点都满足直线表达式y=k*x+b,在计算机上只能用离散点来表示图形来。绘制直线的大致思路是在一条直线的周围找到离直线最近的像素点,这些像素点组成的图形离散的分布在直线周围,当分辨率达到人眼无法识别的时候,人眼看到就是一个连续的直线图形。如果直接用直线表达式y=k*x+b.来绘制直线,想象下当斜率>1时,锯齿状非常明显,极端情况下甚至不可见,下面介绍两种绘制直线的算法。
(一)DDA算法
DDA算法是通过一个点满足的递推关系来得到第二个点的坐标的。
直线表达式:y=k*x+b。
设绘制图形是沿着直线一个端点一个点一个点绘制的,已经绘制第 i个点的坐标(x[i],y[i]), 则:
斜率:k=(y[i+1]-y[i])/(x[i+1]-x[i]) => y[i+1]= y + k*(x[i+1]-x[i])
当 k > =1 时 即:abs( x[i+1]-x[i]) ) > abs( x[i+1]-x[i]) ) 时
X[i+1]=X[i]+1
y[i+1]=y+k
当 K < 1 时 直线比较接近Y轴。所以要在Y的方向上增1.想象X轴和Y轴交换.
Y[i+1]=Y[i]+1
x[i]=x[i]+1/k
代码:
void DrawlineDDA(int x1,int y1,int x2,int y2)
{
double length;
if(abs(x1-x2)>abs(y1-y2))
length=abs(x1-x2);
else
length=abs(y1-y2);
double x=x1;
double y=y1;
double dx=(x2-x1)/length;
double dy=(y2-y1)/length;
int i=0;
while (i<length)
{
glBegin(GL_POINTS);
glColor3f(0,0,0);
glVertex2i(int(x+0.5),int(y+0.5));
glEnd();
x=x+dx;
y=y+dy;
i++;
}
glFlush();
}
由于要进行很多浮点运算,所以为了改进效率,介绍下面的算法
(二) Bresenham算法
Bresenham是比较主流的算法,很多主要的图形库软件直线算法都是用的Bresenham算法
如图
Bresenham 是通过 D1比较D2的长度来确定下一个点的位置
以K<1为例
当D1>D2时,下一个点应该是(X[i+1],y[i+1]).否则下一个点事(x[i+1],y[i]).
K<1 时,已知:X[i+1]=x[i]+1,y[i+1]=y[i]+1,x=x[i]+1
D1= y-y[i] D2=y[i+1]-y
d1-d2 = 2y-y[i]-y[i+1]=2y-y[i]-(y[i]+1) = 2y-2y[i]-1 = 2*(k*x+b)-2y[i]-1 = 2kx-2y[i]-1+2b
= 2k(x[i]+1)-2y[i]-1+2b
设dx=x2-x1,dy=y2-y1
e[i] =(d1-d2)*dx = dx*(d1-d2)=2k(x[i]+1)*dx -2y[i]*dx-dx+2b*dx
= 2*dy*k*x[i]- 2y[i]*dx+2b*dx -dx +2*dy
常数C= 2b*dx -dx +2*dy
e[i]=2*dy*x[i]- 2y[i]*dx (1)
e[i+1]=2*dy*x[i+1]- 2y[i+1]*dx (2)
e[i+1]-e[i] = 2*dy*(x[i+1]-x[i])-2dx(y[i+1]-y[i]) =>e[i+1]= e[i] + 2*dy*(x[i+1]-x[i])-2dx(y[i+1]-y[i])
结论: e[i+1]= e[i] + 2*dy - 2dx(y[i+1]-y[i])
dx=x2-x1>0.e[i]>0 => d1>d2 => y[i+1]=y[i]+1
e[i]<0 => d1 < d2 => y[i+1]=y[i]
得出:0 < k < 1 的情况下
e[i]>=0时 e[i+1]= e[i] + 2*dy - 2dx
e[i]<0, e[i+1] = e[i] + 2*dy
e 初始化 e=2*dy-dx
k 的其他情况可以由图形坐标的对称性得出。
代码
void Bresenham(int x1,int y1,int x2,int y2)
{
int k=0;
if((x2-x1)*(y2-y1)<0)//斜率为负数
{
k=1;
}
int x=x1,y=y1,dx=abs(x2-x1),dy=abs(y2-y1);
bool f=0;
if(dx<dy)//斜率绝对值大于1
{
f=1;
swap(dx,dy);
swap(x,y);
}
int e=2*dy-dx;
glBegin(GL_POINTS);
for(int i=0;i<dx;i++)
{
if(f==1)
glVertex2f(y,x);
else
glVertex2f(x,y);
x++;
if(e>0)
{
e=e+2*(dy-dx);
if(k==0)
y++;
else
{
y--;
}
}
else
{
e=e+2*dy;
}
}
glEnd();
}