计算机图形学 第5章 二维变换与裁剪到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〕的范围内的设备坐标系称为规格化设备坐标系

在这里插入图片描述

  1. 规格化设备坐标系独立于具体输出设备。一旦图形变换到规格化设备坐标系中,只要作一个简单的乘法运算即可映射到具体的设备坐标系中。
  2. 由于规格化设备坐标系能统一用户各种图形的显示范围,故把用户图形变换成规格化设备坐标系中的统一大小标准图形过程叫作图形的逻辑输出
  3. 把规格化设备坐标系中的标准图形送到显示设备上输出的过程叫作图形的物理输出

有了规格化设备坐标系后,图形的输出可以在抽象的显示设备上进行讨论,因而这种图形学又称为与设备无关的图形学



窗口与视区

在观察坐标系中定义的确定显示内容的矩形区域称为窗口
在屏幕坐标系中定义的输出图形的矩形区域称为视区

图形输出需要进行从窗口到视区的变换
只有窗口内的图形才能在视区中输出,
并且输出的形状要根据视区的大小进行调整,这称为窗视变换(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;
         }
}



猜你喜欢

转载自blog.csdn.net/CSDN_YJX/article/details/128798436