二维图像的几何变化
二维图形基本几何变换是指相对于座标原点和座标轴进行的几何变换,包括平移(Translate)、比例(Scale)、旋转(Rotate)、反射(Reflect)和错切(shear)5种变换。物体变换物体变换是通过变换物体上每一个顶点实现的,因此以点的二维基本几何变换为例讲解二维图形基本几何变换矩阵。
平移变换:
比例变换:
对称变换:
旋转变换:
错切变换:
★复合矩阵编程实例:任一图形关于任意的反射轴y=a+bx的反射变换。
解:(1)将座标原点平移到(0,a)处
(2)将反射轴(已平移后的直线)按顺时针方向旋转θ角,使之与x轴重合
(3)图形关于x轴的反射变换
(4)将反射轴逆时针旋转θ角
(5)恢复反射轴的原始位置
因此T=T₁R(-θ)T₂R(θ)T₃
具体代码如下:
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
GLsizei winWidth = 600, winHeight = 600;/* 初始化显示窗口大小 */
GLfloat xwcMin = 0.0, xwcMax = 250.0; /* 设置世界座标系的显示范围 */
GLfloat ywcMin = 0.0, ywcMax = 250.0;
class wcPt2D/* 定义二维点数据结构 */
{
public:
GLfloat x, y;
};
typedef GLfloat Matrix3x3[3][3];
Matrix3x3 matComposite; //定义复合矩阵
void init(void)
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);//设置窗口背景颜色为白色
}
void matrix3x3SetIdentity(Matrix3x3 m) /* 构建3*3的单位矩阵 */
{
GLint i, j;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
m[i][j] = (i == j);
}
void matrix3x3PreMultiply(Matrix3x3 a, Matrix3x3 b) /* 变换矩阵a前乘矩阵b,储存结果到b中 */
{
GLint r, c;
Matrix3x3 temp;
for (r = 0; r < 3; r++)
for (c = 0; c < 3; c++)
temp[r][c] = a[r][0] * b[0][c] + a[r][1] * b[1][c] + a[r][2] * b[2][c];
for (r = 0; r < 3; r++)
for (c = 0; c < 3; c++)
b[r][c] = temp[r][c];
}
void reflectionX()/* 关于x轴对称变换函数*/
{
Matrix3x3 m;
matrix3x3SetIdentity(m);/* 初始化对称矩阵为单位矩阵 */
m[0][0] = 1;
m[1][1] = -1;
m[0][1] = 0;
m[1][0] = 0;
matrix3x3PreMultiply(m, matComposite);/* 将对称矩阵前乘到复合矩阵matComposite中 */
}
void translate2D(GLfloat tx, GLfloat ty)/* 平移变换函数,平移量tx,ty */
{
Matrix3x3 m;
matrix3x3SetIdentity(m);/* 初始化平移矩阵为单位矩阵 */
m[0][2] = tx;
m[1][2] = ty;
matrix3x3PreMultiply(m, matComposite);/* 将平移矩阵前乘到复合矩阵matComposite中 */
}
void rotate2D(wcPt2D pivotPt, GLfloat theta)/* 旋转变换函数,参数为中心点pivotPt和旋转角度theta */
{
Matrix3x3 m;
matrix3x3SetIdentity(m);/* 初始化旋转矩阵为单位矩阵 */
m[0][0] = cos(theta);
m[0][1] = -sin(theta);
m[0][2] = pivotPt.x*(1 - cos(theta)) + pivotPt.y*sin(theta);
m[1][0] = sin(theta);
m[1][1] = cos(theta);
m[1][2] = pivotPt.y*(1 - cos(theta)) - pivotPt.x*sin(theta);
matrix3x3PreMultiply(m, matComposite);/* 将旋转矩阵前乘到复合矩阵matComposite中 */
}
void transformVerts2D(GLint nVerts, wcPt2D * verts)/* 利用复合矩阵计算变换后坐标 */
{
GLint k;
GLfloat temp;
for (k = 0; k < nVerts; k++)
{
temp = matComposite[0][0] * verts[k].x + matComposite[0][1] * verts[k].y + matComposite[0][2];
verts[k].y = matComposite[1][0] * verts[k].x + matComposite[1][1] * verts[k].y + matComposite[1][2];
verts[k].x = temp;
}
}
void triangle(wcPt2D * verts) /* 三角形和直线绘制函数 */
{
GLint k;
glBegin(GL_TRIANGLES);
for (k = 0; k < 3; k++)
glVertex2f(verts[k].x, verts[k].y);
glEnd();
glBegin(GL_LINE_LOOP);
for (k = 3; k < 5; k++)
glVertex2f(verts[k].x, verts[k].y);
glEnd();
}
void displayFcn(void)
{
/* 定义三角形和直线的初始位置 */
GLint nVerts = 5;
wcPt2D verts[5] = { {40.0,14.0},{110.0,14.0},{110.0,55.0} ,{0.0,10.0},{160.0,90.0} };
wcPt2D centroidPt1;
centroidPt1.x = 0;
centroidPt1.y = 0;
wcPt2D pivotPt;
pivotPt = centroidPt1;
GLfloat tx = 0.0, ty1 = -10.0, ty2 = 10.0;
GLdouble theta = -atan((verts[4].y - verts[3].y) / (verts[4].x - verts[3].x));
GLdouble theta1 = atan((verts[4].y - verts[3].y) / (verts[4].x - verts[3].x));
glClear(GL_COLOR_BUFFER_BIT); // 清空显示窗口
glColor3f(0.0, 0.0, 1.0); // 设置前景色为蓝色
triangle(verts); //显示蓝色三角形(变换前)
matrix3x3SetIdentity(matComposite);
/* 初始化复合矩阵为单位矩阵 */
/* 根据变换序列重建复合矩阵 */
translate2D(tx, ty1); //变换序列1:平移变换
rotate2D(pivotPt, theta); //变换序列2:旋转变换
reflectionX(); //变换序列3:对称变换
rotate2D(pivotPt, theta1); //变换序列4:旋转变换
translate2D(tx, ty2);//变换序列5:平移变换
transformVerts2D(nVerts, verts);/* 应用复合矩阵到三角形 */
glColor3f(1.0, 0.0, 0.0); //重新设置前景色为红色
triangle(verts); //显示红色三角形(变换后)
glFlush();
}
void winReshapeFcn(GLint newWidth, GLint newHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(xwcMin, xwcMax, ywcMin, ywcMax);
glClear(GL_COLOR_BUFFER_BIT);
}
void main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(50, 50);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("二维几何变换实例-复合变换");
init();
glutDisplayFunc(displayFcn);
glutReshapeFunc(winReshapeFcn);
glutMainLoop();
}