一、光栅图形扫描转换算法
1.扫描转换一个点
1.1.自动取整法
算法描述:直接取各个点坐标的整数部分。
算法举例:将点P(1.7,0.8)经过自动取整,转化为点(1,0)。
1.2.四舍五入法
算法描述:对点坐标的各个分量进行四舍五入
算法举例:将点P(1.7,0.8)经过四舍五入,转化为点(2,1)。
2.扫描转换一条直线
2.1.数值微分(DDA)算法
算法描述:
①判断直线的最大变化方向:首先计算出直线在X轴方向和Y轴方向各自变化量的绝对值,如果X轴方向变化量的绝对值更大则最大变化方向为X轴,反之最大变化方向为Y轴。
②判断在最大变化方向上两个点的坐标是否满足升序排列,如果不满足,则交换两个点;
③设两个点分别为A(x1,y1)和B(x2,y2),在两点位于主要变化方向上的两个坐标之间进入循环过程:每一次在最大变化方向上+1,而另一个方向上加上直线的斜率K(对最大变化方向为Y轴的情况,加上斜率的倒数1/K),并对求得的坐标点进行扫描转换(自动取整或四舍五入)。
2.2.中点画线算法
算法描述:
①判断直线的最大变化方向:首先计算出直线在X轴方向和Y轴方向各自变化量的绝对值,如果X轴方向变化量的绝对值更大则最大变化方向为X轴,反之最大变化方向为Y轴。
②判断在最大变化方向上两个点的坐标是否满足升序排列,如果不满足,则交换两个点;
③设两点分别为P1(x1,y1),P2(x2,y2),令a=y1-y2,b=x2-x1;
④分情况进行下列循环过程:
1.最大变化方向为X轴,斜率大于零:
int d = 2 * a + b;
int delta1 = 2 * a;
int delta2 = 2 * (a + b);
for (unsigned x = x1, y = y1; x <= x2; ++x)
{
putpixel(x, y, BLUE);
if (d >= 0)
{
d += delta1;
}
else
{
d += delta2;
y++;
}
}
2.最大变化方向为X轴,斜率小于零:
int d = 2 * a - b;
int delta1 = 2 * (a - b);
int delta2 = 2 * a;
for (unsigned x = x1, y = y1; x <= x2; ++x)
{
putpixel(x, y, BLUE);
if (d >= 0)
{
d += delta1;
--y;
}
else
{
d += delta2;
}
}
3.最大变化方向为Y轴,斜率大于零:
int d = a + 2 * b;
int delta1 = 2 * (a + b);
int delta2 = 2 * b;
for (unsigned y = y1, x = x1; y <= y2; ++y)
{
putpixel(x, y, BLUE);
if (d >= 0)
{
d += delta1;
++x;
}
else
{
d += delta2;
}
}
4.最大变化方向为Y轴,斜率小于零:
int d = 2 * b - a;
int delta1 = 2 * b;
int delta2 = 2 * (b - a);
for (unsigned y = y1, x = x1; y <= y2; ++y)
{
putpixel(x, y, BLUE);
if (d >= 0)
{
d += delta1;
}
else
{
d += delta2;
x--;
}
}
注:斜率为零的情况包含在④的1中,斜率不存在的情况包含在④的3中。
2.3.Bresenham算法
算法描述:
①判断直线的最大变化方向:首先计算出直线在X轴方向和Y轴方向各自变化量的绝对值,如果X轴方向变化量的绝对值更大则最大变化方向为X轴,反之最大变化方向为Y轴。
②判断在最大变化方向上两个点的坐标是否满足升序排列,如果不满足,则交换两个点;
③分情况在两点之间进行下列循环过程:(设dx=x2-x1,dy=y2-y1)
1.最大变化方向为X轴,斜率大于零:
int e = -dx;
for (; x <= x2; ++x)
{
putpixel(x, y, YELLOW);
e += 2 * dy;
if (e > 0)
{
y++;
e -= 2 * dx;
}
}
2.最大变化方向为X轴,斜率小于零:
int e = dx;
for (; x <= x2; ++x)
{
putpixel(x, y, YELLOW);
e += 2 * dy;
if (e <= 0)
{
y--;
e += 2 * dx;
}
}
3.最大变化方向为Y轴,斜率大于零:
int e = -dy;
for (; y <= y2; ++y)
{
putpixel(x, y, YELLOW);
e += 2 * dx;
if (e > 0)
{
x++;
e -= 2 * dy;
}
}
4.最大变化方向为Y轴,斜率小于零:
int e = dy;
for (; y <= y2; ++y)
{
putpixel(x, y, YELLOW);
e += 2 * dx;
if (e <= 0)
{
x--;
e += 2 * dy;
}
}
3.扫描转换一个圆
3.1.中点画圆算法
算法描述:(设圆心为(x0,y0))
①变量初始化:令d=1-r,x=0,y=r;
②按照d的正负进行如下的分情况循环(x大于等于y时结束):
作出八个顶点:
(x+x0,y+y0) (x+x0,-y+y0) (-x+x0,y+y0) (-x+x0,-y+y0)
(y+x0,x+y0) (y+x0,-x+y0) (-y+x0,x+y0) (-y+x0,-x+y0)接着进行如下判定:
if (d < 0)
{
d += (2 * x + 3);
x++;
}
else if (d > 0)
{
d += (2 * x - 2 * y + 5);
x++;
y--;
}
3.2.Bresenham画圆算法
算法描述:(设圆心为(x0,y0))
1.初始化x=0,y=r,flag=2-2r。
2.进入循环过程(当y<=0时算法终止):
作出四个顶点:
(x+x0,y+y0) (x+x0,-y+y0) (-x+x0,y+y0) (-x+x0,-y+y0)
确定下一步的绘图方向
if (flag < 0)
{
int flag1 = 2 * (flag + y) - 1;
if (flag1 <= 0)
Direction = 1;
else Direction = 2;
}
else if (flag > 0)
{
int flag2 = 2 * (flag - x) - 1;
if (flag2 <= 0)
Direction = 2;
else Direction = 3;
}
else if(flag==0)
{
Direction = 2;
}
根据上述求出的作图方向进行分情况讨论:
if (Direction == 1)
{
x++;
flag += (2 * x + 1);
}
else if (Direction == 2)
{
x++;
y--;
flag += (2 * x - 2 * y + 2);
}
else if (Direction == 3)
{
y--;
flag += (1 - 2 * y);
}
4.扫描转换一个椭圆(Bresenham算法)
算法描述:(设椭圆的圆心为(x0,y0),半长轴长为a,半短轴长为b)
1.求出P点坐标(a^2/sqrt(a×a+b×b))。
2.从(0,b)到P点部分:
int x = 0, y = b;
double d = b * b + (b - 0.25) * a * a;
for (; x < px; x++)
{
putpixel(x + x0, y + y0, RED);
putpixel(x + x0, -y + y0, RED);
putpixed(-x + x0, y + y0, RED);
putpixed(-x + x0, -y + y0, RED);
if (d <= 0)
{
d += (2 * x + 3) * a * a;
}
else if (d > 0)
{
d += (2 * x + 3) * b * b + (2 - 2 * y) * a * a;
y--;
}
}
3.从P到(a,0)部分:
int x = px, y = py;
double d = b * b + (x + 0.5) * (x + 0.5) + a * a + (y - 1) * (y - 1) - a * a * b * b;
for (; y >= 0; y--)
{
putpixel(x + x0, y + y0, RED);
putpixel(x + x0, -y + y0, RED);
putpixed(-x + x0, y + y0, RED);
putpixed(-x + x0, -y + y0, RED);
if (d <= 0)
{
d += (3 - 2 * y) * a * a + (2 * x + 2) * b * b;
x++;
}
else if (d > 0)
{
d += (3 - 2 * y) * a * a;
}
}
二.二维图形裁剪算法
1.直线段裁剪算法
1.1.Cohen-Suthurland裁剪算法
算法描述:
①求出线段两端点的区域码D3D2D1D0。一个点的区域码为:当点在裁剪窗口上方,则D3=1;当点在裁剪窗口下方,则D2=1;当点在裁剪窗口右边,则D1=1;当点在裁剪窗口左边,则D0=1。
②分情况讨论:
如果两个端点的区域码均为零则说明线段在窗口内,直接绘制即可;
如果两个端点的区域码进行按位与运算的结果不为零,则说明线段位于窗口外,不用绘制;
如果不满足上述两种情况则说明线段与窗口有交点,则需要求出线段与窗口的交点,以交点为分界点将线段分为两段,对两段线段分别递归使用该算法即可。
(求交点的方法:与左右边界的交点用y=y1+k*(x-x1),与上下边界的交点用x=x1+(y-y1)/k)
1.2.中点分割裁剪算法
算法描述:
①求出线段两端点的区域码D3D2D1D0。一个点的区域码为:当点在裁剪窗口上方,则D3=1;当点在裁剪窗口下方,则D2=1;当点在裁剪窗口右边,则D1=1;当点在裁剪窗口左边,则D0=1。
②分情况讨论:
如果两个端点的区域码均为零则说明线段在窗口内,直接绘制即可;
如果两个端点的区域码进行按位与运算的结果不为零,则说明线段位于窗口外,不用绘制;
如果不满足上述两种情况则说明线段与窗口有交点,则需要求出当前线段的中点,以中点为分界点将线段分为两段,对两段线段分别递归使用该算法即可。
1.3.Liang-Barsky裁剪算法
算法描述:
①初始化过程:
令p1=x1-x2,p2=x2-x1,p3=y1-y2,p4=y2-y1;q1=x1-xL,q2=xR-x1,q3=y1-yB,q4=yT-y1;
②判定过程:
如果x1=x2并且q1和q2中至少有一个小于零,则说明直线不在窗口内,无需绘制;
如果y1=y1并且q3和q4中至少有一个小于零,则说明直线不在窗口内,无需绘制;
③令uk=qk/pk(k=1,2,3,4),并定义Umax=max(0,uk|pk<0),Umin=min(1,uk|pk>0);
④如果Umax>Umin,则直线不在窗口内,无需绘制;否则说明直线与窗口相交,分别将Umax和Umin代入x=x1+(x2-x1)×u和y=y1+(y2-y1)×u即可求出两个交点的坐标。
2.多边形裁剪算法
2.1.Sutherland-Hodgman裁剪算法
算法描述:
①按照右下左上的顺序,每次选定窗口的一条边框及其延长线。原始队列就是按照顺时针方向的原始多边形顶点序列的顺时针排列。
②按照顺时针顺序遍历队列中的顶点:如果顶点在当前裁剪边的可见域中或裁剪边上,则将其放入队列中;如果顶点在可见域中或裁剪边上,并且与下一个顶点之间的线段与窗口所选择的边框或其延长线相交,则将交点加入队列中。
③四条边遍历完成后,从最后的顶点序列中找出所有交点,如果处在同一边框上的两个交点相邻,则需要去除这两个点之间的连边。将其它顶点按照顺序连接即可。
2.2.Weiler-Athurton算法
算法描述:
①首先选定多边形的一个顶点,按照顺时针顺序,找出多边形与裁剪窗口的所有交点,并对找到的交点进行分类:如果是从窗口外部走向窗口内部,则称为入点;如果是从窗口内部走向外部,则称为出点。
②再次从多边形的一个顶点开始对多边形进行顺时针行走,记录行走轨迹:如果行走过程中遇到出点,则沿着裁剪窗口按照顺时针方向走到下一个交点处,从下一个交点处开始继续沿着多边形行走。上述过程中获得的行走轨迹就是多边形的裁剪结果。
三.二三维图形的几何变换算法
1.二维图形的几何变换算法
1.1.二维平移变换
算法描述:
设X方向的平移距离为dx,Y方向的平移距离为dy,初始位置为(x,y)
x’=x+dx
y’=y+dy
1.2.二维比例变换
算法描述:
设参考点坐标为(x0,y0),X方向上的比例为Sx,Y方向上的比例为Sy,初始点位置为(x,y)
x’=x×Sx-x0×(Sx-1)
y’=y×Sy-y0×(Sy-1)
1.3.二维对称变换
算法描述:
设对称轴的方程为Ax+By+C=0,变换前点的坐标为(x0,y0)
x’=x0-2A×(Ax0+By0+C)/(A²+B²)
y’=y0-2B×(Ax0+By0+C)/(A²+B²)
1.4.二维旋转变换
算法描述:
设旋转中心为(x0,y0),旋转前的点为(x,y),旋转角度为a(以逆时针方向为正)
则有:
x’=(x-x0)*cos(a)-(y-y0)*sin(a)+x0
y’=(x-x0)*sin(a)+(y-y0)*cos(a)+y0
1.5.二维错切变换
算法描述:
设X方向上的错切系数为a,Y方向上的错切系数为b
x’=x+ay
y’=y+bx
2.三维图形的几何变换算法
2.1.三维平移变换
算法描述:
设X方向的平移距离为dx,Y方向的平移距离为dy,Z方向的平移距离为dz,初始位置为(x,y,z)
x’=x+dx
y’=y+dy
z’=z+dz
2.2.三维比例变换
算法描述:
设参考点坐标为(x0,y0,z0),X方向上的比例为Sx,Y方向上的比例为Sy,Z方向上的比例为Sz,初始点位置为(x,y,z)
x’=x×Sx-x0×(Sx-1)
y’=y×Sy-y0×(Sy-1)
z’=z×Sz-z0×(Sz-1)
2.3.三维旋转变换
算法描述:
设(vx,vy,vz)为旋转轴的单位向量,旋转前的点坐标为(x,y,z),旋转之后的点坐标为(x’,y’,z’),则有:
2.4.三维对称变换
算法描述:
设变换前的点坐标为(x0,y0,z0),变换后为(x,y,z),对称平面的方程为Ax+By+Cz+D=0,则有:
x=x0-2A×(Ax0+By0+Cz0+D)/(A²+B²+C²)
y=y0-2B×(Ax0+By0+Cz0+D)/(A²+B²+C²)
z=z0-2C×(Ax0+By0+Cz0+D)/(A²+B²+C²)
2.5.三维错切变换
算法描述:
设变换前的点坐标为(x0,y0,z0),变换后的点坐标为(x,y,z),则有:
x=x0+ay+bz
y=y0+cx+dz
z=z0+ex+fy
(其中的a,b,c,d,e,f都是错切系数)
四.三维图形的投影与消隐算法
1.平行投影
1.1.三视图
算法描述:
①主视图:令所有点的y值为零,所得到的二维图形就是主视图。变换矩阵如下:
②侧视图:令所有点的x值为零,将所得到的二维图形绕Z轴旋转90度,并将旋转后的图形移动一定距离。总体的变换矩阵如下:
③俯视图:令所有点的z值为零,将所得到的二维图形绕x轴旋转90度,并将旋转后的图形移动一定距离。总体的变换矩阵如下:
1.2.正轴测图
1.2.1.正等轴测图
算法描述:
正等轴测图的变换矩阵如下图所示:
1.2.2.正二轴测图
算法描述:
正二轴测图的变换矩阵如下图所示:
2.透视投影
2.1.单点透视投影
2.2.两点透视投影
2.3.三点透视投影
3.消隐算法
3.1.消除隐藏线算法
算法描述:
①根据三维物体的信息建立单链三表结构。单链三表结构介绍:面表中的每一个元素存储该面在环表中对应的起始下标和结束下标;环表中的每一个元素存储该面所有的顶点的编号(按照对外的一面的顺时针方向);点表存储每一个顶点的空间三维坐标(一个顶点可能被重复存储多次)。
②根据视轴,将三维物体的每一个顶点进行坐标变换得到投影点坐标。
③根据视轴,判定物体的每一个面的可见性,判定方法如下:对于一个对外的面的顶点的顺时针序列,按照逆时针方向找出相邻的三个顶点A(x1,y1,z1),B(x2,y2,z2),C(x3,y3,z3),计算外法线向量AB×BC=(y1z2-y2z1,z1x2-z2x1,x1y2-x2y1)。如果外法向量与视轴的点积大于零则该面可见,小于等于零则该面不可见。
④对于可见的面,绘制出其投影后的多边形边框。
3.2.交点检验算法
算法描述:
①从该点向下作一条与Z轴平行的射线。
②如果射线与平面图形的交点为偶数个则说明该点在多边形之外,如果交点个数为奇数个则说明该点在多边形之内。(如果射线刚好经过了多边形的顶点,则采用“左闭右开”的原则,即边在射线左边则不计数,边在射线的右边才计数)
3.3.夹角和检验算法(高效版本)
算法描述:
①将坐标系的坐标原点移动到被测点,此时多边形的每一个顶点相对于新的坐标原点,都有了新的象限。
②规定位于第一象限内的点角度为零,第二象限内的点角度为二分之派,第三象限内的点角度为派,第四象限内的点角度为负二分之派。
③将多边形所有顶点对应的角度进行求和,如果角度之和为0则说明该点在多边形之外,如果角度之和为二派则说明该点在多边形内部。
3.4.画家算法
算法描述:
当场景中有多个物体时,先画远处的物体,再画近处的物体,这样一来近处的物体就可以覆盖远处的物体。
3.5.Z缓冲区算法
算法描述:
①建立一个二维数组,用于存储屏幕上每一个像素的深度坐标,初始化每一个深度坐标为深度的最小值。初始化屏幕上每一个像素的颜色为背景色。
②逐一检验每一个多边形:如果当前多边形在该点的深度值大于该点当前的深度值,则将该点的深度值修改为该多边形在这一点的深度值,同时将该点的颜色改为该多边形在这一点的颜色。
五.曲线绘制算法
1.三次样条曲线绘制算法
算法描述:
三次样条曲线是由一段段短小的曲线进行光滑连接生成的,每一段曲线都是抛物线。第i段曲线的计算公式为:
Pi+1(t)=(-2t³+4t²-t)Pi+(12t³-410t²+1)Pi+1+(-12t³+8t²+t)Pi+2+(4t³-2t²)Pi+3
对于结尾处缺少的两条曲线,有两种添加辅助点的方法:
①添加两个辅助点P0=P1,Pn+1=Pn,这样可以得到一条不闭合的自由曲线;
②添加三个辅助点P0=Pn,Pn+1=P1和Pn+2=P2,由此可以得到一条闭合的曲线。
2.贝塞尔曲线绘制算法
算法描述:设有N个型值点,第i个型值点的坐标为(xi,yi)
①进入最外层循环,初始化t=0,从零开始自增(步长自行控制);
②以选定的t值,求出特征多边形上每两条相邻的的线段的t分割点,每一次都会使得特征多边形的边数减少1;
③当特征多边形只留下一条边时结束循环,求出来的等比例点就在贝塞尔曲线上。
3.B样条曲线绘制算法
算法描述:
根据下面的公式即可计算B样条曲线的函数表达式
也可以通过下面的公式进行递推: