计算机图形学 - 实验:Tow-Dimensional Geometric Transforamation


本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,有所改动。大部分内容来自本人实验报告,有错误是难以避免的,若有表述错误或bug欢迎指出。

实验思路

根据教材上的变换矩阵,借助二维数组,依次写出生成旋转、缩放、平移矩阵的函ScalingMatrix()RotationMatrix()TranslationMatrix()。每种变换都有一个生成变换矩阵函数和一个变换函数相对应Translate(),Rotate(),Scale()。在变换函数中会调用生成变换矩阵函数,并在特殊键调用函数中根据按下键的不同调用不同的变换函数。在进行矩阵乘法运算时,左乘或者右乘体现在代码上,是三重循环时的对两个二维数组的遍历方式。为了避免变换矩阵未能及时复原成单位矩阵的问题,在每个生成变换矩阵函数中都采用定义一个临时矩阵,对临时矩阵进行修改变换后,再赋值给变换矩阵,所以需要将变换矩阵定义为全局变量,并且为了安全起见,将变换矩阵初始化为单位矩阵

实现代码

核心代码及关键步骤注释

class CPoint2D {
public:
	float x, y;
};

// 2D transformation matrix
float My2DMatrix[3][3]; //全局变换矩阵

// Generate translation matrix
void TranslationMatrix(float tx, float ty, float M[3][3]) { //生成平移矩阵
	// 矩阵为
	// |1  0 tx|
	// |0  1 ty|
	// |0  0  1|
	M[0][0] = 1.0;
	M[0][1] = 0.0;
	M[0][2] = tx; //横坐标平移量

	M[1][0] = 0.0;
	M[1][1] = 1.0;
	M[1][2] = ty; //纵坐标平移量

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Generate rotation matrix
void RotationMatrix(float theta, float M[3][3]) {
	// 矩阵为
	// |cos(x) -sin(x) 0|
	// |sin(x)  cos(x) 0|
	// |     0       0 1|
	//x为旋转角
	M[0][0] = cos(theta);
	M[0][1] = -sin(theta);
	M[0][2] = 0;

	M[1][0] = sin(theta);
	M[1][1] = cos(theta);
	M[1][2] = 0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Generate scaling matrix
void ScalingMatrix(float sx, float sy, float M[3][3]) { //生成缩放矩阵
	// 矩阵为
	// |sx 0 0|
	// |0 sy 0|
	// |0  0 1|
	M[0][0] = sx;
	M[0][1] = 0.0;
	M[0][2] = 0;

	M[1][0] = 0.0;
	M[1][1] = sy;
	M[1][2] = 0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Translate
void Translate(float tx, float ty)
// tx, ty --- Translation vector
{
	float M[3][3];
	TranslationMatrix(tx, ty, M); //变换矩阵
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; //临时矩阵
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) //i、j两重循环用来遍历矩阵中的每个元素
			for (int k = 0; k < 3; k++) { //把对应的元素遍历相乘再相加
				T[i][j] += M[i][k] * My2DMatrix[k][j];
			}
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j]; 
		}
}

// Rotate
void Rotate(float theta)
// theta --- Rotation angle in degree
{
	float M[3][3];
	RotationMatrix(theta, M); //生成旋转变换矩阵
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++) {
				T[i][j] += M[i][k] * My2DMatrix[k][j]; //和平移矩阵相同
			}

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j]; //更改全局变换矩阵
		}
}
// Scale
void Scale(float sx, float sy)
// sx, sy --- Scaling factors
{ //同上方其他变换函数
	float M[3][3];
	ScalingMatrix(sx, sy, M);
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++) {
				T[i][j] += M[i][k] * My2DMatrix[k][j];
			}

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j];
		}
}

// Transform 2D point
void Transform2DPoint(CPoint2D* p, float M[3][3], CPoint2D* q)//3x1矩阵和3x3矩阵的相乘
// p --- Input point
// M --- Transformation matrix
// q --- Output point
{
	q->x = M[0][0] * p->x + M[0][1] * p->y + M[0][2];
	q->y = M[1][0] * p->x + M[1][1] * p->y + M[1][2];
}

void IdentityMatrix(float M[3][3]) { //初始化为单位矩阵
	// 矩阵为
	// |1 0 0|
	// |0 1 0|
	// |0 0 1|
	M[0][0] = 1.0;
	M[0][1] = 0.0;
	M[0][2] = 0.0;

	M[1][0] = 0.0;
	M[1][1] = 1.0;
	M[1][2] = 0.0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

全部代码

// ====== Computer Graphics Experiment #5 ======
// |       Two-Dimensional Transformation      |
// =============================================
//
// Requirement:
// (1) Implement functions to generate 2D transformation matrix.
// (2) Implement function to transform 2D point using
//     transformation matrix.
// (3) Implement functions to rotate, scale and translate objects
//     using keyboard.

#include 
#include 
#include 

#define PI 3.1415926535
// 2D point class
class CPoint2D {
public:
	float x, y;
};

// 2D transformation matrix
float My2DMatrix[3][3]; //全局变换矩阵

// Generate translation matrix
void TranslationMatrix(float tx, float ty, float M[3][3]) { //生成平移矩阵
	// 矩阵为
	// |1  0 tx|
	// |0  1 ty|
	// |0  0  1|
	M[0][0] = 1.0;
	M[0][1] = 0.0;
	M[0][2] = tx; //横坐标平移量

	M[1][0] = 0.0;
	M[1][1] = 1.0;
	M[1][2] = ty; //纵坐标平移量

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Generate rotation matrix
void RotationMatrix(float theta, float M[3][3]) {
	// 矩阵为
	// |cos(x) -sin(x) 0|
	// |sin(x)  cos(x) 0|
	// |     0       0 1|
	//x为旋转角
	M[0][0] = cos(theta);
	M[0][1] = -sin(theta);
	M[0][2] = 0;

	M[1][0] = sin(theta);
	M[1][1] = cos(theta);
	M[1][2] = 0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Generate scaling matrix
void ScalingMatrix(float sx, float sy, float M[3][3]) { //生成缩放矩阵
	// 矩阵为
	// |sx 0 0|
	// |0 sy 0|
	// |0  0 1|
	M[0][0] = sx;
	M[0][1] = 0.0;
	M[0][2] = 0;

	M[1][0] = 0.0;
	M[1][1] = sy;
	M[1][2] = 0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Translate
void Translate(float tx, float ty)
// tx, ty --- Translation vector
{
	float M[3][3];
	TranslationMatrix(tx, ty, M); //变换矩阵
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; //临时矩阵
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) //i、j两重循环用来遍历矩阵中的每个元素
			for (int k = 0; k < 3; k++) { //把对应的元素遍历相乘再相加
				T[i][j] += M[i][k] * My2DMatrix[k][j];
			}
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j]; 
		}
}

// Rotate
void Rotate(float theta)
// theta --- Rotation angle in degree
{
	float M[3][3];
	RotationMatrix(theta, M); //生成旋转变换矩阵
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++) {
				T[i][j] += M[i][k] * My2DMatrix[k][j]; //和平移矩阵相同
			}

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j]; //更改全局变换矩阵
		}
}
// Scale
void Scale(float sx, float sy)
// sx, sy --- Scaling factors
{ //同上方其他变换函数
	float M[3][3];
	ScalingMatrix(sx, sy, M);
	float T[3][3] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			for (int k = 0; k < 3; k++) {
				T[i][j] += M[i][k] * My2DMatrix[k][j];
			}

	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++) {
			My2DMatrix[i][j] = T[i][j];
		}
}

// Transform 2D point
void Transform2DPoint(CPoint2D* p, float M[3][3], CPoint2D* q)//3x1矩阵和3x3矩阵的相乘
// p --- Input point
// M --- Transformation matrix
// q --- Output point
{
	q->x = M[0][0] * p->x + M[0][1] * p->y + M[0][2];
	q->y = M[1][0] * p->x + M[1][1] * p->y + M[1][2];
}

void IdentityMatrix(float M[3][3]) { //初始化为单位矩阵
	// 矩阵为
	// |1 0 0|
	// |0 1 0|
	// |0 0 1|
	M[0][0] = 1.0;
	M[0][1] = 0.0;
	M[0][2] = 0.0;

	M[1][0] = 0.0;
	M[1][1] = 1.0;
	M[1][2] = 0.0;

	M[2][0] = 0.0;
	M[2][1] = 0.0;
	M[2][2] = 1.0;
}

// Initialization function
void init(void) { //初始化函数
	glClearColor(0.0, 0.5, 0.5, 0.0); //设置颜色
	IdentityMatrix(My2DMatrix);
}

// Display callback function
void display(void) {
	static CPoint2D MyObject[] = { { 0.0, 63.0 },
		{ -60.0, 20.0 }, { -60.0, -20.0 },
		{ 60.0, -20.0 }, { 60.0, 20.0 } };
	CPoint2D pp;
	int i;

	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(.3, 0.3, 0.3);

	glBegin(GL_POLYGON);
	for (i = 0; i < 5; ++i) {
		Transform2DPoint(MyObject + i, My2DMatrix, &pp);
		glVertex2f(pp.x, pp.y);
	}
	glEnd();

	glutSwapBuffers();
}

// Reshape callback function
void reshape(int w, int h) {
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-100, 100, -100, 100);
}

// Keyboard callback function
void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case 27:
		exit(0);
	case '=': //放大,设置成等号,若设置成加号需要配合shift键才能放大
		Scale(1.5, 1.5);
		glutPostRedisplay();
		break;
	case '-':
		Scale(0.75, 0.75);
		glutPostRedisplay();
		break;
	}
}

// Special keyboard callback function
void special_key(int key, int x, int y) {
	switch (key) {
	case GLUT_KEY_LEFT:
		Translate(-5.0, 0.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_RIGHT:
		Translate(5.0, 0.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_DOWN:
		Translate(0.0, -5.0);
		glutPostRedisplay();
		break;
	case GLUT_KEY_UP:
		Translate(0.0, 5.0);
		glutPostRedisplay();
		break;
		// Add your code for rotation and scaling
	case GLUT_KEY_PAGE_UP://放大1.5倍
		Scale(1.5, 1.5);
		glutPostRedisplay();
		break;
	case GLUT_KEY_PAGE_DOWN://缩小0.5倍大小
		Scale(0.5, 0.5);
		glutPostRedisplay();
		break;
	case GLUT_KEY_HOME://逆时针旋转90度
		Rotate(PI / 18);
		glutPostRedisplay();
		break;
	case GLUT_KEY_END://顺时针旋转90度
		Rotate(-PI / 18);
		glutPostRedisplay();
		break;
	case GLUT_KEY_INSERT://旋转180度
		Rotate(PI);
		glutPostRedisplay();
		break;
	}
}

// Main program entrance
int main(int argc, char* argv[]) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("Zhy's Test 2D Transformation");
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(special_key);
	glutMainLoop();
	return 0;
}