计算机图形学笔记——第7章 二维几何变换 Python

第7章 二维几何变换

应用于对象几何描述并改变它的位置、方向或大小的操作称为几何变换(geometric transformation)

几何变换有时也称为建模变换(modeling transformation),但有些图形系统将两者区分开来。建模变换一般用于构造场景或给出由多个部分组合而成的复杂对象的层次式描述等。

基本的二维几何变换

平移、旋转和缩放是所有图形软件包中都包含的几何变换函数。可能包括在图形软件包中的其他变换函数有反射和错切操作。

二维平移

通过将位移量加到一个点的座标上来生成一个新的座标位置,可以实现一次平移(translation)。实际上,我们将该点从原始位置沿一直线路径移动到新位置。

将平移距离(translation distance)txt_xtyt_y加到原始座标(x,y)(x,y)上获得一个新的座标位置(xy)(x^{'},y^{'}),可以实现一个二维位置的平移。
x=x+txy=y+ty x^{' } = x +t_x \quad y^{' } = y +t_y
一对平移距离(tx,ty)(t_x,t_y)称为平移向量(translation vector)位移向量(shift vector)

在这里插入图片描述
如果用矩阵表示
P=[xy]P=[xy]T=[txty] P = \begin{bmatrix}x \\ y \end{bmatrix} \quad P^{'} = \begin{bmatrix}x^{'} \\ y^{'} \end{bmatrix} \quad T = \begin{bmatrix}t_x \\t_ y \end{bmatrix}
这样就可以使用矩阵形式来表示二维平移方程:
P=P+T P^{'} = P+T
下面的程序演示了平移操作。输入的平移向量用来将一个多边形的n个顶点从世界座标系的一个位置移动到另一个位置,而OpenGL子程序用来重新生成平移后的多边形。

class wcPt2D(
	public: GLfloat x,y;
}; 
void translatePolygon (wcPt2D"verts, GLint nVerts, GLfloat tx, GLfloat ty){
	GLint k; 
          for (k=0;k<nVerts:k++){
              verts [k].x = verts [k].x + tx; 
              verts [k].y =verts [k].y + ty; 
          }
           g1Begin (GL_POLYGON); 
                       for (k=0;k< nVerts;k++)
			g1Vertex2f (verts [k].x, verts [k].y); 
           g1End();
}

二维旋转

通过指定一个旋转轴(rotation axis)和一个旋转角度(rotation angle),可以进行一次旋转(rotation)变换。在将对象的所有顶点按指定角度绕指定旋转轴旋转后,该对象的所有点都旋转到新位置。

对象的二维旋转通过在xyxy平面上沿圆路径将对象重定位来实现。此时,我们将对象绕与xyxy平面垂直的旋转轴(与zz轴平行)旋转。二维旋转的参数有旋转角θ\theta和称为旋转点(rotation point 或pivot point)的位置(xr,yr)(x_r,y_r),对象绕该点旋转。基准点是旋转轴与xyxy平面的交点。正角度θ\theta定义绕基准点的逆时针旋转,而负角度将对象沿顺时针方向旋转。

为了简化模型,我们先假设基准点为原点。
在这里插入图片描述

因此旋转后用角度θ\thetaϕ\phi表示为
x=rcos(ϕ+θ)=rcosϕcosθrsinϕsinθy=rsin(ϕ+θ)=rcosϕsinθ+rsinϕcosθ x^{'} = r\cos(\phi + \theta) = r \cos \phi \cos\theta - r\sin \phi \sin \theta \\ y^{'} = r\sin(\phi + \theta) = r\cos\phi\sin\theta + r \sin\phi \cos \theta
在极座标系中,点的原始座标为
x=rcosϕy=rsinϕ x = r \cos \phi \quad y = r\sin \phi
所以可以得到:
x=xcosθysinθy=xsinθ+ycosθ x^{'} = x\cos \theta - y\sin \theta \\ y^{'}= x \sin \theta +y \cos \theta
如果用矩阵的形式表示:
P=RP P^{'} = R\cdot P
其中,旋转矩阵为:
R=[cosθsinθsinθcosθ] R = \begin{bmatrix}\cos \theta & -\sin\theta \\ \sin\theta & \cos \theta\end{bmatrix}
但现在OpenGL、Java、PHIGS和GKS都按标准列向量方式表示。

任意的旋转位置(xr,yr)(x_r,y_r)旋转点的变换方程:
x=xr+(xxr)cosθ(yyr)sinθy=yr+(xxr)sinθ+(yyr)cosθ x^{'} = x_r + (x-x_r)\cos \theta - (y-y_r)\sin\theta \\ y^{'} = y_r + (x-x_r) \sin \theta + (y-y_r)\cos \theta
线段的旋转可以通过用于每个线段端点,并重新绘制新端点间的线段而得到。多边形的旋转则是将每个顶点旋转指定的旋转角,并使用新的顶点来生成多边形而实现旋转。曲线的旋转通过重新定位定义的点并重新绘制曲线而完成。例如圆或椭圆,可以通过将中心位置沿指定旋转角对着的弧移动而绕非中心轴旋转。椭圆可通过旋转其长轴和短轴来实现绕其中心位置的旋转。

class wcPt2D {}
	public:
		GLfloat x,y; 
}; 
void rotatePolygon (wcPt2D* verts, GLint nVerts, wePt2D pivPt, GLdouble theta)
{
    wcPt2D* vertsRot; 
    GLint k; 
    for (k=0;k< nVerta:k++){
	vertsRot [k].x=pivPt.x+(verts [k].x-pivPt.x) * cos (theta)-(verts [k].y-pivpt.y)* sin (theta); 
         vertsRot [k].y=pivPt.y+(verts [k],x-pivPt.x )* sin (theta)+(verts [k].y-pivPt.y)* cos (theta):
    ]
    g1Begin { GL_POLYGON}: 
        for(k=0;k<nVerts;k++)
  		g1Vertex2f (verteRot [k].x, verteRot [k].y): 
    g1End();

二维缩放

改变一个对象的大小,可使用缩放(scaling)变换。一个简单的二维缩放操作可通过将缩放系数(scaling factor)sxs_xsys_y,与对象座标位置(x,y)(x,y)相乘而得:
x=xsxy=ysy x^{'} = x \cdot s_x \quad y^{'} = y\cdot s_y
矩阵形式如下
[xy]=[sx00sy][xy] \begin{bmatrix}x^{'} \\ y^{'}\end{bmatrix} = \begin{bmatrix}s_x & 0 \\ 0 & s_y\end{bmatrix}\cdot \begin{bmatrix}x \\ y\end{bmatrix}
或者是
P=SP P^{'} = S\cdot P
sxs_xsys_y相同的值的时候,就会产生保持对象相对比例一致的一致缩放(uniform scaling)。当两个值不相等的时候就是差值缩放(differential scaling)。

当缩放系数的绝对值小于1时,缩放后的对象向原点靠近;而缩放系数绝对值大于1时,缩放后的座标位置远离原点。

我们可以选择一个在缩放变换后不改变位置的点,称为固定点(fixed point),以控制缩放后对象的位置。固定点的座标(xf,yf)(x_f,y_f)可以选择对象的中点等位置或任何其他空间位置。这样,多边形通过缩放每个顶点到固定点的距离而相对于固定点进行缩放。对于座标为(x,y)(x,y)的顶点,缩放后的座标(x,y)(x^{'},y^{'})可计算为
xxf=(xxf)sxyyf=(yyf)sy x^{'} - x_f = (x-x_f)s_x \quad y^{'} - y_f = (y-y_f)s_y
可以得到
x=xsx+xf(1sx)y=ysy+yf(1sy) x^{'} = x\cdot s_x + x_f(1-s_x)\\ y^{'} = y\cdot s_y + y_f(1-s_y)
其中xf(1sx)x_f(1-s_x)yf(1sy)y_f(1-s_y)都是常数。

在这里插入图片描述

class wePt2D {
    public: 
    GLfloat x,y; 
};
void scalePolygon (wcPt2D· verts, GLint nVerts, wcpt2D fixedPt, GLfloat sx, GLfloat sy)
{
    wcPt2D vertsNew; 
    GLint k; 
    for (k-0;k<nVerts;k++){
        vertsNew[k].x=verts [k].x* sx+ fixedPt.x*(1-sx); 
        vertaNew[k].y=verts [k].y*sy+fixedPt.y*(1: sy): 
         
    } 
    g1Begin (GL POLYGON]; 
             for (k-0;k<nVerte;k++) 
             	g1Vertex2f (vertsNew [k].x, vertsNew [k].y); 
     g1End():

矩阵表示和齐次座标

每个基本变换(平移、旋转和缩放)都可以表示为普通矩阵形式
P=M1P+M2 P^{'} = M_1P+M_2
PPPP^{'}都是座标的列向量。矩阵M1M_1是一个包含乘法系数的2×22\times 2的矩阵,M2M_2是包含平移项的两元素列矩阵。

对于平移,M1M_1是单位矩阵。对于旋转或缩放,M2M_2包含与基准点或缩放固定点相关的平移项。

齐次座标

如果将2×22\times 2矩阵表达式扩充为3×33\times 3矩阵,就可以把二维儿何变换的乘法和平移组合成单一矩阵表示。这时将变换矩阵的第三列用于平移项,而所有的变换公式可表达为矩阵乘法。但为了这样操作,必须解释二维座标位置到三元列向量的矩阵表示。标准的实现技术是将二维座标位置表示(x,y)(x,y)扩充到三维表示(xh,yh,h)(x_h,y_h,h),称为齐次座标(homogeneous coordinate),这里的齐次参数(homogeneous parameter)h是一个非零值,因此
x=xhhy=yhh x = \frac{x_h}{h} \quad y = \frac{y_h}{h}
普通的二维齐次座标表示可写为(h×x,h×y,h)(h\times x , h\times y,h)。最简单的h=1h=1。因此每个二维位置都可以用齐次座标来表示(x,y,1)(x,y,1)来表示。

二维平移矩阵

使用齐次座标方法,座标位置的二维平移可表示为下面的矩阵乘法:
[xy1]=[10tx01ty001]×[xy1] \begin{bmatrix}x^{'} \\ y^{'} \\ 1\end{bmatrix} = \begin{bmatrix}1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 &1\end{bmatrix} \times \begin{bmatrix}x \\y \\ 1\end{bmatrix}
该平移操作可简写为
P=T(tx,ty)P P^{'} = T(t_x,t_y)P

二维旋转矩阵

二维的旋转变换公式为
[xy1]=[cosθsinθ0sinθcosθ0001][xy1] \begin{bmatrix}x^{'} \\ y^{'} \\ 1 \end{bmatrix} = \begin{bmatrix}\cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0\\0 & 0 &1\end{bmatrix} \begin{bmatrix}x \\ y \\ 1\end{bmatrix}
其中旋转变换操作R(θ)R(\theta)的旋转参数θ\theta

二维缩矩阵

相对于座标原点的缩放变换可以表示为
[xy1]=[sx000sy0001][xy1] \begin{bmatrix}x^{'} \\ y^{'} \\ 1\end{bmatrix} = \begin{bmatrix}s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0& 0 & 1\end{bmatrix} \begin{bmatrix}x \\ y \\ 1\end{bmatrix}

逆变换

对于平移变换,我们通过对平移距离取负值而得到逆矩阵。因此,如果二维平移距离是txt_xtyt_y,则其逆平移矩阵是

T1=[10tx01ty001] T^{-1} = \begin{bmatrix}1 & 0 & -t_x \\ 0 & 1 & -t_y \\ 0 & 0 & 1\end{bmatrix}

逆旋转通过用旋转角度的负角取代该旋转角来实现。例如,绕座标系原点的角度为0的二维旋转有如下的逆变换矩阵
R1=[cosθsinθ0sinθcosθ0001] R^{-1} = \begin{bmatrix} \cos \theta & \sin \theta & 0 \\ -\sin \theta & \cos\theta & 0 \\ 0 & 0 & 1\end{bmatrix}
将缩放系数用其倒数取代就得到了缩放变换的逆矩阵。对以座标系原点为中心、缩放参数为sxs_xsys_y的二维缩放,其逆变换矩阵为
S1=[1sx0001sy0001] S^{-1} = \begin{bmatrix}\frac{1}{s_x} & 0 & 0 \\ 0 & \frac{1}{s_y}& 0 \\ 0 & 0 &1\end{bmatrix}

二维复合变换

利用矩阵表达式,可以通过计算单个变换的矩阵乘积,将任意的变换序列组成复合变换矩阵(composite transformation matrix)。形成变换矩阵的乘积经常称为矩阵的合并(concatenation) 或复合(composition)。由于一个座标位置用齐次列矩阵表示,我们必须用表达任意变换顺序的矩阵来前乘该列矩阵。由于场景中许多位置用相同的顺序变换,先将所有变换矩阵相乘形成一个复合矩阵将是高效率的方法。因此,如果我们要对点位置P进行两次变换,变换后的位置将用下式计算:
P=M2M1P=MP P^{'} = M_2M_1P = MP

复合二维平移

假如将两个连续的平移向量(t1x,t1y)(t_{1x},t_{1y})(t2x,t2y)(t_{2x},t_{2y})用于座标位置P,那么最后的变换位置PP^{'}可以计算为
P=T(t2x,t2y){T(t1x,T1y)P}={T(t1x,t1y)T(t2x,t2y)}P=T(t1x+t2x,t1y+t2y) P^{'} = T(t_{2x},t_{2y})\{T(t_{1x},T_{1y})P\} = \{T(t_{1x},t_{1y})T(t_{2x},t_{2y})\}P = T(t_{1x}+t_{2x},t_{1y}+t_{2y})
这表示两个连续平移是相加的。

复合二维旋转

P=R(θ2){R(θ1)P}={R(θ1)R(θ2)}P P^{'} = R(\theta_2)\{R(\theta_1)P\} = \{R(\theta_1)R(\theta_2)\}P

并且
R(θ1)R(θ2)=R(θ1+θ2) R(\theta_1) R(\theta_2) = R(\theta_1+\theta2)

复合二维缩放

[s2x000s2y0001][s1x000s1y0001]=[s1xs2x000s1ys2y0001] \begin{bmatrix}s_{2x} & 0 & 0 \\ 0 & s_{2y} & 0 \\ 0 & 0 &1\end{bmatrix} \begin{bmatrix}s_{1x} & 0 & 0 \\ 0 & s_{1y}& 0 \\ 0 & 0 & 1\end{bmatrix} = \begin{bmatrix}s_{1x}s_{2x} & 0 &0 \\ 0 & s_{1y}s_{2y} & 0 \\ 0 & 0 &1\end{bmatrix}

或者是
S(s2x,s2y)S(s1x,s1y)=S(s1xs2x,s1ys2y) S(s_{2x},s_{2y})\cdot S(s_{1x},s_{1y}) = S(s_{1x}\cdot s_{2x} , s_{1y}\cdot s_{2y})

通用二维基准点旋转

当图形软件包仅提供绕座标系原点的旋转函数时,我们可通过完成下列平移-旋转-平移操作序列来实现绕任意选定的基准点(xr,yr)(x_r,y_r)的旋转。

  1. 平移对象使基准点位置移动到座标原点;
  2. 绕座标原点旋转;
  3. 平移对象使基准点回到其原始位置。

[10xr01yr001][cosθsinθ0sinθcosθ0001][10xr01yr001]=[cosθsinθxr(1cosθ)+yrsinθsinθcosθyr(1cosθ)xrsinθ001] \begin{bmatrix}1 & 0 & x_r \\0 & 1 & y_r \\ 0 & 0 & 1\end{bmatrix}\cdot \begin{bmatrix}\cos \theta & -\sin \theta & 0 \\\sin\theta & \cos \theta & 0\\ 0 & 0 & 1\end{bmatrix}\cdot \begin{bmatrix}1 & 0 & -x_r \\0 & 1 & -y_r \\ 0 & 0 & 1\end{bmatrix} = \begin{bmatrix}\cos\theta & -\sin\theta & x_r(1-\cos\theta) + y_r\sin \theta \\\sin\theta & \cos\theta & y_r(1-\cos\theta)-x_r\sin\theta \\ 0 & 0 & 1\end{bmatrix}

该等式可以使用下列形式表示:
T(xr,yr)R(θ)T(xr,yr)=R(xr,yr,θ) T(x_r,y_r)R(\theta)T(-x_r,-y_r) = R(x_r,y_r,\theta)
其中T(xr,yr)=T1(xr,yr)T(-x_r, -y_r) = T^{-1}(x_r,y_r)

在这里插入图片描述

通用二维基准点缩放

在只有相对于座标原点缩放的缩放函数时,缩放的变换序列:

  1. 平移对象使固定点与座标原点重合;
  2. 对于座标原点进行缩放;
  3. 使用步骤1的反向平移将对象返回到原始位置。

[10xf01yf001][sx000sy0001][10xf01yf001]=[sx0xf(1sx)0syyf(1sy)001] \begin{bmatrix}1& 0 & x_f \\ 0 & 1 & y_f \\ 0 & 0 &1\end{bmatrix}\cdot \begin{bmatrix}s_x& 0 &0 \\ 0 & s_y & 0 \\ 0 & 0 &1\end{bmatrix}\cdot \begin{bmatrix}1& 0 & -x_f \\ 0 & 1 & -y_f \\ 0 & 0 &1\end{bmatrix} = \begin{bmatrix}s_x& 0 & x_f(1-s_x) \\ 0 & s_y & y_f(1-s_y) \\ 0 & 0 &1\end{bmatrix}\cdot


T(xf,yf)S(sx,sy)T(xf,yf)=S(xf,yf,sx,sy) T(x_f,y_f)\cdot S(s_x,s_y) \cdot T(-x_f,-y_f) = S(x_f,y_f,s_x,s_y)

在这里插入图片描述

通用二维定向缩放

参数sxs_xsys_y沿x和y方向缩放对象,可以通过在应用缩放变换之前,将对象所希望的缩放方向旋转到与座标轴一致来实现在其他方向上缩放对象。

首先完成旋转操作,使s1s_1s2s_2的方向分别与x和y轴重合。然后应用缩放变换S(s1,s2)S(s_1,s_2),再进行反向旋转回到其原始位置。从这三个变换的乘积得到的复合矩阵为
R1(θ)S(s1,s2)R(θ)=[s1cos2θ+s2sin2θ(s2s1)cosθsinθ0(s2s1)cosθsinθs1sin2θ+s2cos2θ0001] R^{-1}(\theta) \cdot S(s_1,s_2)\cdot R(\theta) = \begin{bmatrix} s_1\cos^2\theta+s_2\sin^2\theta & (s_2-s_1)\cos\theta\sin\theta & 0 \\ (s_2-s_1)\cos\theta\sin\theta & s_1\sin^2\theta + s_2\cos^2\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}

矩阵合并特性

矩阵相乘符合结合律,对于任何三个矩阵M1M_1M2M_2M3M_3。矩阵积M3M2M1M_3M_2M_1可以写成
M3M2M1=(M3M2)M1=M3(M2M1) M_3M_2M_1 = (M_3M_2)M_1 = M_3(M_2M_1)
因此,依靠变换的描述顺序,我们既可以使用从左到右(前乘),也可以使用从右到左(后乘)的结合分组来求矩阵乘积。有些图形软件包要求变换按应用的顺序描述。

二维刚体变换

如果一个变换矩阵仅包含平移和旋转参数,则它是一个刚体变换矩阵(rigid-body transformation matrix)。二维刚体变换矩阵的一般形式为
[rxxrxytrxryxryytry001] \begin{bmatrix} r_{xx} & r_{xy} & tr_{x} \\ r_{yx} & r_{yy} & tr_y \\ 0 & 0 & 1 \end{bmatrix}
其中,4个元素rjkr_{jk}是多重旋转项,元素trxtr_xtrytr_y是平移项。座标位置的刚体变换有时也称为刚体运动(rigid motion)。变换后的座标位置间的所有角度和距离都不变化。

因此上述左上角的2×22\times 2矩阵是一个正交矩阵(orthogonal matrix)的特性。说明,如果将子矩阵的每一行(或每一列)作为向量,那么两个行向量(rxx,rxy)(r_{xx}, r_{xy})(ryx,ryy)(r_{yx},r_{yy})形成单位向量的正交组。这样的一组向量也称为正交向量组。每个向量具有单位长度。
rxx2+rxy2=ryx2+ryy2=1 r^2_{xx} + r^2_{xy} = r^2_{yx} + r^2_{yy} = 1
并且向量相互垂直
rxxryx+rxyryy=0 r_{xx}r_{yx} + r_{xy}r_{yy} = 0

二维复合矩阵编程

其他二维变换

反射

产生对象镜像的变换称为反射(reflection)。对于二维反射而言,其反射镜像通过将对象绕反射轴旋转180180^{\circ}而生成。我们选择的反射轴(axis of reflection)可以是在xyxy平面内的一条直线或者是垂直xyxy平面的一条直线。当反射轴是xyxy平面内的一条直线时,绕这个轴的旋转路径在垂直于xyxy平面的平面中;而对于垂直于xyxy平面的反射轴,旋转路径在xyxy平面内。下面举出一些普通的反射例子。

关于直线y=0y=0(xx轴)的反射,可以由下列的变换矩阵完成。
[100010001] \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 1 \end{bmatrix}
对于x=0x=0(y轴)的反射,翻动xx的座标而保持yy座标不变,这种变换的矩阵是
[100010001] \begin{bmatrix} -1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}

在这里插入图片描述
关于xyxy的平面内任意直线y=mx+by=mx+b 的反射,可以使用平移-旋转-反射变换的组合来完成的。

通常,我们先平移直线使其经过原点。然后将直线旋转到座标轴之一,并进行相对于座标轴的反射。最后利用逆旋转和逆平移变换将直线还原到原来位置。

错切

错切(shear) 是一种使对象形状发生变化的变换,经过错切的对象好像是由已经相互滑动的内部夹层组成。两种常用的错切变换是移动xx座标值的错切和移动yy座标值的错切。

相对于xx轴的xx方向错切由下列变换矩阵产生:
[1shx0010001] \begin{bmatrix} 1 & sh_x & 0 \\ 0 & 1 & 0 \\ 0 & 0 &1 \end{bmatrix}
该矩阵将座标位置转换成
x=x+shxyy=y x^{'} = x + sh_x \cdot y \quad y^{'} = y
可以将任意实数赋给错切参数shsh.。然后将座标位置(x,y)(x,y)水平地移动与其到xx轴的距离(yy值)成正比的量。

在这里插入图片描述
错切操作可以表示为基本变换的序列。

几何变换的光栅方法

光栅系统的特殊功能为特定的二维变换提供了另一种方法。光栅系统将图像信息作为颜色图案存储在帧缓冲器中。控制矩形像素数组的光栅功能通常称为光栅操作(raster operation),将一块像素从一个位置移动到另一个位置的过程也称为像素值的块移动(block transfer,bitblt或pixblt)。图形软件中通常包含完成某些光栅操作的子程序。

90°倍数的旋转可以很容易地利用重新安排像素矩阵的元素而实现。通过首先将阵列的每一行的像素值颠倒,然后交换其行和列来将对象逆时针旋转90°;通过颠倒阵列的每一行中元素的顺序,然后将行的顺序颠倒来得到180°的旋转。

像素块的光栅缩放采用类似方法实现。我们用指定的sxs_xsys_y值对原始块中的像素区域进行缩放,并将缩放的矩形映射到一组目标像素上,然后按照其与缩放像素区域的重叠区域,设置每个目标像素的亮度。

OpenGL光栅变换

像素颜色值的矩形数组从一个缓存到另一个的平移可以作为如下的OpenGL复制操作来完成:

g1CopyPixe1s (xmin, ymin, width, height. GL_COLOR):

前面4个参数给出了像素块的位置和尺寸。而OpenGL符号常量GL_COLOR指定要复制的颜色值。该像素数组复制到刷新缓存中由当前光栅位置指定的左下角的一个矩形区域内。像素颜色值依赖于颜色模式的当前设定,按RGBA或颜色表进行复制。提供复制的区域(源)和复制目标区域均应位于屏幕座标边界内。该平移可作用于任何刷新缓存或不同缓存之间。g1CopyPixels函数的源缓存用g1ReadBuffer 子程序选择,而目标缓存用g1DrawBuffer子程序选择。

缓存中的一个RGB颜色块可以用下列函数存入一个数组:

g1ReadPixels (xmin, ymin, width. height, GL_RGB.GL_UNSIGNED_BYTE, colorArray):

如果颜色表索引存于像素位置,则将GL_COLOR_INDEX取代GL_RGB。为了旋转颜色值,必须如前一节所述重新安排颜色数组的行与列。然后使用下列语句将旋转后的数组放回缓存:

g1DrawPixe1s (width,height,GL_RGB,GL_UNSIGNED_BYTE, colorArray);

该数组的左下角放到当前光栅位置。我们用g1ReadBuffer选择包含原来的像素值块的源缓存,用g1DrawBuffer指定目标缓存。

二维缩放变换通过指定缩放因子然后引用g1Copypixe1s或g1Drawpixe1s按OpenGL中的光栅操作来完成。对于光栅操作,使用下列函数来设定缩放因子:

g1Pixe1zoom(sx,sy):

这里,参数sx和sy可赋以任何非零浮点值。大于1.0的正值增太源数组元素的尺对,而小于1.0的正值减少元素尺寸。sx或sy中有负值或两个都为负值则生成该数组元素的反射及缩放。因此,如果sx=sy=-3.0,则源数组相对于当前光栅位置反射且数组的每一颜色元素映射到目标缓存中的3×3像素块。如果目标像素的中心位于一数组缩放颜色元素的矩形区域,则用该数组元素给它赋值。中心在缩放数组元素左边界或上边界的目标像素也赋以该元素的颜色。sx和sy的默认值均为1.0。

二维座标系间的变换

非笛卡儿系统的例子有极座标系统、球面座标系统、椭圆座标系统和抛物线座标系统。

给出了一个在笛卡儿座标系xyxy中用座标原点(x0,y0)(x_0,y_0)及方向角θ\theta指定的笛卡儿座标系xyx^{'}y^{'}。为了将对象描述从xyxy座标变换到xyx^{'}y^{'}座标,必须建立把xyx^{'}y^{'}轴叠加到xyxy轴的变换,这需要分两步进行:

  1. xyx^{'}y^{'}系统的座标原点(x0,y0)(x_0,y_0)平移到xyxy系统的原点(0,0)(0,0)
  2. xx'轴旋转到xx轴上

座标原点的平移可以使用下列矩阵操作表示:
T(x0,y0)=[10x001y0001] T(-x_0,-y_0) = \begin{bmatrix} 1 & 0 & -x_0 \\ 0 & 1 & -y_0 \\ 0 & 0 & 1 \end{bmatrix}
为了将两个系统的轴重合,可以顺时针旋转:
R(θ)=[cosθsinθ0sinθcosθ0001] R(-\theta) = \begin{bmatrix} \cos \theta & \sin \theta & 0 \\ -\sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix}
把这两个变换矩阵合并起来,就给出了将对象描述从xyxy系统转换到xyx^{'}y^{'}系统的完整复合矩阵。
Mxy,xy=R(θ)T(x0,y0) M_{xy,x^{'}y^{'}} = R(-\theta)T(-x_0,-y_0)

在这里插入图片描述

OpenGL二维几何变换函数

在OpenGL的核心库中,每一种基本的几何变换都有一个独立的函数。由于OpenGL是作为三维图形应用编程接口(APl)来设计的,所有变换都在三维空间中定义。在内部,所有座标均使用4元素列向量表示,而所有变换均使用4×4矩阵表示。因此,二维变换可以通过在OpenGL中选择使第三维(z)不改变的z值来实现。

基本的OpenGL几何变换

4×44\times 4平移矩阵用下列子程序构造

g1Translate*(tx,ty,tz)

平移参数tx、ty和tz可赋予任意的实数值,附加于该函数的单个后缀码或者是f(浮点)或者是d。

旋转矩阵4×44\times 4下列函数生成:

glRotate*(theta,vx,vy,vz)

向量v=vxvyvzv=(vx,vy,vz)的分量可以有任意的浮点数值。该向量用于定义通过座标原点的旋转轴的方向。

用下列函数可得到相对于座标原点的4×4缩放矩阵:

glScale*(sx, sy, sz)

OpenGL矩阵操作

将该矩阵看做建模观察矩阵(modelview matrix),它用于存储和组合几何变换,也用于将几何变换与向观察座标系的变换进行组合。建模观察模式用下列语句指定:

g1MatrixMode (GL_MODELVIEW)

该语句指定一个4×4建模观察矩阵作为当前矩阵(current matrix)

在这个调用后的OpenGL变换子程序用来修改建模观察矩阵,而后该矩阵用来变换场景中的座标位置。用g1MatrixMode函数还可以设定另外两个模式:纹理模式(texture mode)颜色模式(color mode)。纹理模式用于映射表面的纹理图案,而颜色模式用于从一个颜色模型转换到另一个。后面几章将讨论观察、投影、纹理和颜色变换。

建立建模观察模式(或任何其他模式)后,调用变换子程序所生成的矩阵要与该模式的当前矩阵相乘。另外,我们可以对当前矩阵的元素赋值,OpenGL库中有两个函数可用于此目的。使用下列函数可设定当前矩阵为单位矩阵:

glLoadIdentity():

也可以为当前矩阵的元素赋其他值:

g1LoadMatrix*(elements16):

参数elements16指定了一个单下标、16元素的浮点值数组,而后缀f或d用来指定数据类型。该数组的元素必须按列优先顺序指定。即先列出第一列的4个元数,接着列出第二列的4个元素,然后是第三列,而最后是第四列。

也可以将指定的矩阵与当前矩阵合并:

glMultMatrix*(otherElements16):

实例

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import math

winWidth, winHeight = 600, 600
xwcMin, xwcMax = 0.0, 300.0
ywcMin, ywcMax = 0.0, 300.0
pi = 3.14159
matComposite = [[0.0, 0.0, 0.0],
                [0.0, 0.0, 0.0],
                [0.0, 0.0, 0.0]]

def init():
    glClearColor(1.0, 1.0, 1.0, 0.0)


def get3x3Matfloat():
    temp = [[0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0]]
    return temp


# 矩阵初始化
def matrix3x3SetIndentity(matIdent3x3):
    for row in range(3):
        for col in range(3):
            matIdent3x3[row][col] = (row == col)

# 显示三角形
def triangle(verts):
    glBegin(GL_TRIANGLES)
    for k in range(3):
        glVertex2f(verts[k][0], verts[k][1])
    glEnd()


def matrix3x3PreMultiply(m1, m2):
    mattemp = get3x3Matfloat()
    for row in range(3):
        for col in range(3):
            mattemp[row][col] = m1[row][0] * m2[0][col] + m1[row][1] * m2[1][col] + m1[row][2] * m2[2][col]

    for row in range(3):
        for col in range(3):
            m2[row][col] = mattemp[row][col]

def rotate2D(pivotPt, theta):
    global matComposite
    matRot = get3x3Matfloat()
    matrix3x3SetIndentity(matRot)
    matRot[0][0] = math.cos(theta)
    matRot[0][1] = -math.sin(theta)
    matRot[0][2] = pivotPt[0] * (1-math.cos(theta)) + pivotPt[1] * math.sin(theta)

    matRot[1][0] = math.sin(theta)
    matRot[1][1] = math.cos(theta)
    matRot[1][2] = pivotPt[0] * (1-math.cos(theta)) - pivotPt[1]*math.sin(theta)

    matrix3x3PreMultiply(matRot, matComposite)

def scale2D(sx, sy, fixedPt):
    global matComposite
    matScale = get3x3Matfloat()
    matrix3x3SetIndentity(matScale)
    matScale[0][0] = sx
    matScale[0][2] = (1-sx)*fixedPt[0]
    matScale[1][1] = sy
    matScale[1][2] = (1-sy) * fixedPt[1]
    matrix3x3PreMultiply(matScale, matComposite)


def translate2D(tx, ty):
    global matComposite
    matTrans1 = get3x3Matfloat()
    matrix3x3SetIndentity(matTrans1)
    matTrans1[0][2] = tx
    matTrans1[1][2] = ty
    matrix3x3PreMultiply(matTrans1, matComposite)

def transformVerts2D(nVerts, verts):
    global matComposite
    for k in range(nVerts):
        temp = matComposite[0][0] * verts[k][0] + matComposite[0][1] * verts[k][1] + matComposite[0][2]
        verts[k][1] = matComposite[1][0] * verts[k][0] + matComposite[1][1] * verts[k][1] + matComposite[1][2]
        verts[k][0] = temp

def dispalyFcn():
    global pi,matComposite
    nVerts = 3
    verts = [[50.0, 25.0],[150.0, 25.0],[100.0, 100.0]]
    xsum , ysum = 0,0
    for k in range(nVerts):
        xsum += verts[k][0]
        ysum += verts[k][1]
    centroidPt = [0,0]
    centroidPt[0] = xsum/nVerts
    centroidPt[1] = ysum/nVerts
    pivPt = centroidPt[:]
    fixedPt = centroidPt[:]
    tx = 0.0
    ty = 100.0
    sx = 0.5
    sy = 0.5
    theta = pi/2.0
    glClear(GL_COLOR_BUFFER_BIT)
    glColor3f(0.0, 0.0, 1.0)
    triangle(verts)
    matrix3x3SetIndentity(matComposite)

    scale2D(sx, sy, fixedPt)

    rotate2D(pivPt, theta)

    translate2D(tx, ty)

    transformVerts2D(nVerts, verts)

    glColor3f(1.0, 0.0, 0.0)
    triangle(verts)
    glFlush()



def winReshapeFcn(newWidth, newHeight):
    global xwcMax, xwcMin, ywcMax, ywcMin
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax)
    glClear(GL_COLOR_BUFFER_BIT)


if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
    glutInitWindowPosition(50, 50)
    glutInitWindowSize(winWidth, winHeight)
    glutCreateWindow("几何变换".encode('gbk'))
    init()
    glutDisplayFunc(dispalyFcn)
    glutReshapeFunc(winReshapeFcn)
    glutMainLoop()

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章