计算机图形学 - 实验: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;
}