目录
- 学习目标
- 前驱知识
- 规范化齐次坐标
- 二维几何变换矩阵
- 物体变换与坐标变换
- 二维几何变换
- 二维图形基本几何变换矩阵
- 平移变换矩阵
- 比例变换矩阵
- 旋转变换矩阵
- 反射变换矩阵
- 错切变换矩阵
- 二维仿射变换
- 二维复合变换
- 相对于任意方向的二维几何变换
- 二维图形裁剪
- 世界坐标系
- 2.用户坐标系
- 观察坐标系
- 屏幕坐标系
- 设备坐标系
- 窗口与视区
- 窗视变换矩阵
- 裁剪算法
- Cohen-Sutherland直线裁剪算法(科恩-萨瑟兰)
- 裁剪步骤
- 交点计算公式
学习目标
- 了解齐次坐标的概念。
- 熟练掌握二维基本几何变换矩阵。
- 熟练掌握Cohen-Sutherland直线段裁剪算法。
- 掌握中点分割直线段裁剪算法。
- 掌握Liang-Barsky直线段裁剪算法。
- 了解Sutherland-Hodgman多边形裁剪算法。
前驱知识
图形几何变换是对图形进行平移变换、比例变换、旋转变换、反射变换和错切变换。
图形几何变换可以分为二维图形几何变换和三维图形几何变换(暂时两种),而二维图形几何变换是三维图形几何变换的基础 。
规范化齐次坐标
二维几何变换矩阵
物体变换与坐标变换
二维几何变换
图形顶点集合的规范化齐次坐标矩阵 与 某一变换矩阵 相乘的形式。变换矩阵的定义看式(5-2)
二维图形基本几何变换矩阵
二维图形基本几何变换是指相对于坐标原点和坐标轴进行的几何变换,包括平移(Translate)、比例(Scale)、旋转(Rotate)、反射(Reflect)和错切(shear)5种变换。
物体变换是通过变换物体上每一个顶点实现的,因此以点的二维基本几何变换为例讲解二维图形基本几何变换矩阵 。
平移变换矩阵
比例变换矩阵
旋转变换矩阵
反射变换矩阵
错切变换矩阵
举个例子
记住这都是怎么错切的。可以只记变换矩阵。
一些规律:
上面讨论的五种变换给出的都是点变换的公式,对于线框模型,图形的变换实际上都可以通过点变换来完成。
例如直线段的变换可以通过对两个顶点坐标进行变换,连接新顶点得到变换后的新直线段;多边形的变换可以通过对每个顶点进行变换,连接新顶点得到变换后的新多边形。曲线的变换可通过变换控制多边形的控制点后,重新绘制曲线来实现。
二维仿射变换
符合下面形式的坐标变换称为二维仿射变换(Affine Transformation)。
仿射变换具有平行线变换成平行线,有限点映射到有限点的一般特性。
平移、比例、旋转、反射和错切五种变换都是二维仿射变换的特例,任何一组二维仿射变换总可表示为这5种变换的组合。
二维复合变换
原理:
相对于任一参考点的比例变换和旋转变换应表达为复合变换形式,
变换方法为(两步)
- 首先将参考点平移到坐标原点,对坐标原点进行比例变换和旋转变换,
- 然后再进行反平移将参考点平移回原位置。
上面右边的图是先把Q和P点们移动到坐标原点。
相对于任意方向的二维几何变换
相对于任意方向的变换方法是
- 首先对“任意方向”做旋转变换,使变换方向与坐标轴重合,
- 然后对坐标轴进行二维基本几何变换,
- 最后做反向旋转变换,将任意方向还原到原来的方向。
例5-2 将图示三角形相对于轴线y=kx+b作反射变换,计算每一步的变换矩阵。
首先对“任意方向”做旋转变换,使变换方向与坐标轴重合。↑
然后对坐标轴进行二维基本几何变换↓
最后做反向旋转变换,将任意方向还原到原来的方向。↓
代码:
class CTransform//二维几何变换
{
public:
CTransform();
virtual ~CTransform();
void SetMat(CP2 *,int);
void Identity();
void Translate(double,double);//平移变换矩阵
void Scale(double,double);//比例变换矩阵
void Scale(double,double,CP2);//相对于任意点的比例变换矩阵
void Rotate(double);//旋转变换矩阵
void Rotate(double,CP2);//相对于任意点的旋转变换矩阵
void ReflectO();//原点反射变换矩阵
void ReflectX();//X轴反射变换矩阵
void ReflectY();//Y轴反射变换矩阵
void Shear(double,double);//错切变换矩阵
void MultiMatrix();//矩阵相乘
public:
double T[3][3];
CP2 *POld;
int num;
};
void CTransform::Identity()//单位矩阵
{
T[0][0]=1.0;T[0][1]=0.0;T[0][2]=0.0;
T[1][0]=0.0;T[1][1]=1.0;T[1][2]=0.0;
T[2][0]=0.0;T[2][1]=0.0;T[2][2]=1.0;
}
void CTransform::Translate(double tx,double ty)//平移变换矩阵
{
Identity();
T[2][0]=tx;
T[2][1]=ty;
MultiMatrix();
}
void CTransform::Scale(double sx,double sy)//比例变换矩阵
{
Identity();
T[0][0]=sx;
T[1][1]=sy;
MultiMatrix();
}
void CTransform::Rotate(double beta)//旋转变换矩阵
{
Identity();
double rad=beta*PI/180;
T[0][0]=cos(rad); T[0][1]=sin(rad);
T[1][0]=-sin(rad);T[1][1]=cos(rad);
MultiMatrix();
}
void CTransform::Rotate(double beta,CP2 p)//相对于任意点的旋转变换矩阵
{
Translate(-p.x,-p.y);
Rotate(beta);
Translate(p.x,p.y);
}
void CTransform::ReflectO()//原点反射变换矩阵
{
Identity();
T[0][0]=-1;
T[1][1]=-1;
MultiMatrix();
}
void CTransform::ReflectX()//X轴反射变换矩阵
{
Identity();
T[0][0]=1;
T[1][1]=-1;
MultiMatrix();
}
void CTransform::ReflectY()//Y轴反射变换矩阵
{
Identity();
T[0][0]=-1;
T[1][1]=1;
MultiMatrix();
}
void CTransform::Shear(double b,double c)//错切变换矩阵
{
Identity();
T[0][1]=b;
T[1][0]=c;
MultiMatrix();
}
void CTransform::MultiMatrix()//矩阵相乘
{
CP2 *PNew=new CP2[num];
for(int i=0;i<num;i++)
{
PNew[i]=POld[i];
}
for(int j=0;j<num;j++)
{
POld[j].x=PNew[j].x*T[0][0]+PNew[j].y*T[1][0]
+PNew[j].w*T[2][0];
POld[j].y=PNew[j].x*T[0][1]+PNew[j].y*T[1][1]
+PNew[j].w*T[2][1];
POld[j].w=PNew[j].x*T[0][2]+PNew[j].y*T[1][2]
+PNew[j].w*T[2][2];
}
delete []PNew;
}
二维图形裁剪
世界坐标系
描述现实世界中场景的固定坐标系称为世界坐标系,
世界坐标系是实数域坐标系,
根据应用的需要可以选择直角坐标系、圆柱坐标系、球坐标系以及极坐标系等。
2.用户坐标系
描述物体数学模型的坐标系称为用户坐标系,有时也称为局部坐标系。
用户坐标系也是实数域坐标系。
用户坐标系是可移动坐标系,用户坐标系的原点可以放在物体的任意位置上,坐标系也可以旋转任意角度。
对于立方体,可以将用户坐标系原点放置在立方体中心;对于圆柱,可以将用户坐标系的y轴作为旋转轴。
观察坐标系
观察坐标系是在世界坐标系中定义的坐标系,观察坐标系原点位于视点,z轴垂直于屏幕,正向为视线方向。
二维观察坐标系主要用于指定图形的输出范围。
三维观察坐标系是左手系,用于生成物体的旋转动画。
屏幕坐标系
屏幕坐标系为实数域二维直角坐标系。原点位于屏幕中心,x轴水平向右为正,y轴垂直向上为正。
设备坐标系
显示器等图形输出设备自身都带有一个二维直角坐标系称为设备坐标系。设备坐标系是整数域二维坐标系,原点位于屏幕左上角,x轴水平向右,y轴垂直向下,基本单位为像素。格化到〔0.0,0.0〕到〔1.0,1.0〕的范围内的设备坐标系称为规格化设备坐标系。
- 规格化设备坐标系独立于具体输出设备。一旦图形变换到规格化设备坐标系中,只要作一个简单的乘法运算即可映射到具体的设备坐标系中。
- 由于规格化设备坐标系能统一用户各种图形的显示范围,故把用户图形变换成规格化设备坐标系中的统一大小标准图形的过程叫作图形的逻辑输出。
- 把规格化设备坐标系中的标准图形送到显示设备上输出的过程叫作图形的物理输出。
有了规格化设备坐标系后,图形的输出可以在抽象的显示设备上进行讨论,因而这种图形学又称为与设备无关的图形学。
窗口与视区
在观察坐标系中定义的确定显示内容的矩形区域称为窗口。
在屏幕坐标系中定义的输出图形的矩形区域称为视区。
图形输出需要进行从窗口到视区的变换,
只有窗口内的图形才能在视区中输出,
并且输出的形状要根据视区的大小进行调整,这称为窗视变换(Window Viewport Transformation,WVT)。
窗口概念已广泛用于图形系统中,泛指任何可以移动,改变大小、激活或变为无效的屏幕上的矩形区域。
在计算机图形学术语中,窗口回归到其的原始定义,是在观察坐标系中确定输出图形范围的矩形区域。
窗视变换矩阵
用到了上面变换矩阵的知识。
裁剪算法
Cohen-Sutherland直线裁剪算法(科恩-萨瑟兰)
最早流行的编码算法。
在二维观察中,需要在观察坐标系下根据窗口大小对二维图形进行裁剪(clipping),只将位于窗口内的图形变换到视区输出。
直线段裁剪是二维图形裁剪的基础,裁剪的实质是判断直线段是否与窗口相交,如相交则进一步确定直线段上位于窗口内的部分。
每段直线的端点都被赋予一组四位二进制代码,称为区域编码(region code,RC),用来标识直线段端点相对于窗口边界及其延长线的位置。
为了保证 窗口内 及 窗口边界上 直线段端点 的 编码 为 零,定义规则如下:
这个不好记啊,尤其是学了那么多东西之后。
裁剪步骤
(3)若直线段既不满足“简取”也不满足“简弃”的条件,则需要与窗口进行“求交”判断。这时,直线段必然与窗口边界或窗口边界的延长线相交,分两种情况处理。
直线与窗口边界相交的情况
直线与窗口边界延长线相交的情况
交点计算公式
代码
#define LEFT 1 //代表:0001
#define RIGHT 2 //代表:0010
#define BOTTOM 4 //代表:0100
#define TOP 8 //代表:1000
void CTestView::EnCode(CP2 &pt)//编码函数
{
pt.rc=0;
if(pt.x<Wxl)
pt.rc=pt.rc | LEFT;
else if(pt.x>Wxr)
pt.rc=pt.rc | RIGHT;
if(pt.y<Wyb)
pt.rc=pt.rc | BOTTOM;
else if(pt.y>Wyt)
pt.rc=pt.rc | TOP;
}
#define LEFT 0x1 //代表:0001
#define RIGHT 0x2 //代表:0010
#define BOTTOM 0x4 //代表:0100
#define TOP 0x8 //代表:1000
int EnCode(double x , double y , int Wxl , int Wxr , int wyb , int wyt)
{
rc = 0;
if(x < Wxl) rc = rc | LEFT;
if(x > Wxr) rc = rc | RIGHT;
if(y < Wyb) rc = rc | BOTTOM;
if(y > Wyt) rc = rc | TOP;
return rc ;
}
void CTestView::Cohen()//Cohen-Sutherland算法
{
CP2 p;//交点坐标
EnCode(P[0]);//起点编码
EnCode(P[1]);//终点编码
while(P[0].rc!=0 || P[1].rc!=0)//处理至少一个顶点在窗口外
{
if((P[0].rc & P[1].rc)!=0)//简弃之
{
PtCount=0;
return;
}
if(0==P[0].rc)//确保P[0]位于窗口之外
{
CP2 Temp;
Temp=P[0];
P[0]=P[1];
P[1]=Temp;
}
UINT RC=P[0].rc;
double k=(P[1].y-P[0].y)/(P[1].x-P[0].x);//斜率
if(RC & LEFT)//P[0]点位于窗口的左侧
{
p.x=Wxl;//计算交点y坐标
p.y=k*(p.x-P[0].x)+P[0].y;
}
else if(RC & RIGHT)//P[0]点位于窗口的右侧
{
p.x=Wxr;//计算交点y坐标
p.y=k*(p.x-P[0].x)+P[0].y;
}
else if(RC & BOTTOM)//P[0]点位于窗口的下侧
{
p.y=Wyb;//计算交点x坐标
p.x=(p.y-P[0].y)/k+P[0].x;
}
else if(RC & TOP)//P[0]点位于窗口的上侧
{
p.y=Wyt;//计算交点x坐标
p.x=(p.y-P[0].y)/k+P[0].x;
}
EnCode(p);
P[0]=p;
}
}