天天看點

計算機圖形學-三維物體的平移、旋轉、防縮

要求

  1. 利用OpenGL函數畫一個三維物體;
  2. 利用滑鼠或鍵盤控制三維物體在螢幕上移動、旋轉和放縮

    (1)移動:不能直接調用OpenGL幾何變換函數,運用齊次坐标,采用矩陣相乘的方式自己程式設計實作;

    (2)旋轉、放縮可以調用OpenGL函數

使用工具:

  1. Visual Studio C++控制台
  2. opengl

效果圖:

計算機圖形學-三維物體的平移、旋轉、防縮
計算機圖形學-三維物體的平移、旋轉、防縮

實作步驟:

1.基本三維物體繪制:旋轉坐标系,用線條表示出x軸、y軸和z軸,然後畫出基本的三維物體,調整旋轉系數檢視三維物體是否繪制成功,如圖1所示;

2. 平移功能:運用其次坐标,矩陣相乘及代碼,實作平移功能;

3. 旋轉和縮放功能:使用OpenGL函數glRotatef和glScalef實作;

4.鍵盤設定:UP、DOWN、LEFT和RIGHT分别控制物體的上、下、左和右移動,PAGE_UP 和PAGE_DOWN分别控制物體在y軸方向的放大和縮小,INSERT控制物體圍繞y軸旋轉,END讓物體回到初始狀态。

知識點:

  1. 矩陣相乘遵循的規則:

    M x N x K = (M X N) x K = M x (N x K) M x N 不等于 N x M

  2. 平移矩陣設定:
    計算機圖形學-三維物體的平移、旋轉、防縮
    計算機圖形學-三維物體的平移、旋轉、防縮
  3. 旋轉矩陣設定:
    計算機圖形學-三維物體的平移、旋轉、防縮
    計算機圖形學-三維物體的平移、旋轉、防縮
  4. 縮放矩陣設定:
    計算機圖形學-三維物體的平移、旋轉、防縮
    計算機圖形學-三維物體的平移、旋轉、防縮

部分功能代碼:

/* 矩陣初始化,正對角線上設為1,其餘為0 */
void Matrix4x4SetIdentity(Matrix4x4 matIdent4x4)
{
    GLint row, col;

    for (row = 0; row < 4; row++)
        for (col = 0; col < 4; col++)
            matIdent4x4[row][col] = (row == col);
}

/* 兩個矩陣相乘,結果存儲到矩陣m2;
   如果隻是使用矩陣實作平移,可以省去這個步驟,
   若是要用矩陣全部實作平移、旋轉、縮放,則需要一個中間矩陣,最後将某點的中間矩陣與原坐标矩陣進行相乘,得出新的點坐标
*/
void Matrix4x4PreMultiply(Matrix4x4 m1, Matrix4x4 m2)
{
    GLint row, col;
    Matrix4x4 matTemp;

    for (row = 0; row < 4; row++)
        for (col = 0; col < 4; col++)
            matTemp[row][col] = m1[row][0] * m2[0][col] + m1[row][1] *
            m2[1][col] + m1[row][2] * m2[2][col] + m1[row][3] * m2[3][col];

    for (row = 0; row < 4; row++)
        for (col = 0; col < 4; col++)
            m2[row][col] = matTemp[row][col];
}

/* 平移 */
void Translate3D(GLfloat tx, GLfloat ty, GLfloat tz)
{
    Matrix4x4 matTransl;
    Matrix4x4SetIdentity(matTransl);
    matTransl[0][3] = tx;
    matTransl[1][3] = ty;
    matTransl[2][3] = tz;
    Matrix4x4PreMultiply(matTransl, matComposite);
}

/*
旋轉,全方位旋轉
 */
void rotate3D (wcPt3D p1, wcPt3D p2, GLfloat radianAngle)
{
   Matrix4x4 matQuatRot;
   float axisVectLength = sqrt ((p2.x - p1.x) * (p2.x - p1.x) +
                        (p2.y - p1.y) * (p2.y - p1.y) +
                        (p2.z - p1.z) * (p2.z - p1.z));
   float cosA = cosf (radianAngle);
   float oneC = 1 - cosA;
   float sinA = sinf (radianAngle);
   float ux = (p2.x - p1.x) / axisVectLength;
   float uy = (p2.y - p1.y) / axisVectLength;
   float uz = (p2.z - p1.z) / axisVectLength;

   translate3D (-p1.x, -p1.y, -p1.z);

   matrix4x4SetIdentity (matQuatRot);

   matQuatRot [0][0] = ux*ux*oneC + cosA;
   matQuatRot [0][1] = ux*uy*oneC - uz*sinA;
   matQuatRot [0][2] = ux*uz*oneC + uy*sinA;
   matQuatRot [1][0] = uy*ux*oneC + uz*sinA;
   matQuatRot [1][1] = uy*uy*oneC + cosA;
   matQuatRot [1][2] = uy*uz*oneC - ux*sinA;
   matQuatRot [2][0] = uz*ux*oneC - uy*sinA;
   matQuatRot [2][1] = uz*uy*oneC + ux*sinA;
   matQuatRot [2][2] = uz*uz*oneC + cosA;

   matrix4x4PreMultiply (matQuatRot, matComposite);
   translate3D (p1.x, p1.y, p1.z);
}
//僅對X軸旋轉
void Rotate3Dx(wcPt3D pivotPt, GLfloat theta)
{
    Matrix4x4 matRot;

    /*  Initialize rotation matrix to identity.  */
    Matrix4x4SetIdentity(matRot);

    matRot[1][1] = cos(theta);
    matRot[1][2] = sin(theta);
    matRot[2][1] = -sin(theta);
    matRot[2][2] = cos(theta);

    Matrix4x4PreMultiply(matRot, matComposite);
}
//僅對Y軸旋轉
void Rotate3Dy(wcPt3D pivotPt, GLfloat theta)
{
    Matrix4x4 matRot;

    /*  Initialize rotation matrix to identity.  */
    Matrix4x4SetIdentity(matRot);

    matRot[0][0] = cos(theta);
    matRot[0][2] = sin(theta);
    matRot[2][0] = -sin(theta);
    matRot[2][2] = cos(theta);

    Matrix4x4PreMultiply(matRot, matComposite);
}

/*縮放*/
void scale3D (Gfloat sx, GLfloat sy, GLfloat sz, wcPt3D fixedPt)
{
   Matrix4x4 matScale3D;
   matrix4x4SetIdentity (matScale3D);
   matScale3D [0][0] = sx;
   matScale3D [0][3] = (1 - sx) * fixedPt.x;
   matScale3D [1][1] = sy;
   matScale3D [1][3] = (1 - sy) * fixedPt.y;
   matScale3D [2][2] = sz;
   matScale3D [2][3] = (1 - sz) * fixedPt.z;
   matrix4x4PreMultiply (matScale3D, matComposite);
}
           

執行個體代碼:

#include <GL/glut.h>
#include<stdlib.h>
#include<math.h>

GLsizei winWidth = , winHeight = ;
GLint nVerts = ;//點的個數
GLfloat tx=, ty=, tz=;//存儲要平移的距離
GLfloat angle = ;//旋轉角度
GLfloat scale = ;//縮放比例
typedef GLfloat M4[][];
M4 matComposite;
class pt3D { public: GLfloat x, y, z; };
pt3D verts[] = {
        { , ,  }, { -, -,  }, { , -,  },
        { , ,  }, { , -,  }, { , -, - },
        { , ,  }, { , -, - }, { -, -, - },
        { , ,  }, { -, -, - }, { -, -,  }
};//存放三維物體的各個點坐标,由于有4個三角形面,是以有12個點,設定頂點一緻就好,拼接三角形,底面不做繪制
pt3D resultVerts[];//存放變換後的矩陣,即最新的點坐标
class color { public: GLfloat r, g, b; };
color colors[] = {
        { , ,  }, { , ,  }, { , ,  }, { , ,  }
};//存放每個面的顔色

void init()
{
    glClearColor(, , , );
    glOrtho(-, , -, , -, );
    glMatrixMode(GL_PROJECTION);
}
void m4SetIdentity(M4 matIdentity4x4)
{
    GLint col, row;
    for (row = ; row < ; row++){
        for (col = ; col < ; col++){
            matIdentity4x4[row][col] = (row == col);
        }
    }
}
void m4PreMultiply(M4 m1, M4 m2)
{
    GLint row, col;
    M4 matTemp;
    for (row = ; row < ; row++){
        for (col = ; col < ; col++){
            matTemp[row][col] = m1[row][] * m2[][col] + m1[row][] * m2[][col] + m1[row][] * m2[][col] + m1[row][] * m2[][col];
        }
    }
    for (row = ; row < ; row++){
        for (col = ; col < ; col++){
            m2[row][col] = matTemp[row][col];
        }
    }
}
void translate3D(GLfloat tx, GLfloat ty, GLfloat tz)
{
    M4 matTranslate3D;
    m4SetIdentity(matTranslate3D);
    matTranslate3D[][] = tx;
    matTranslate3D[][] = ty;
    matTranslate3D[][] = tz;
    m4PreMultiply(matTranslate3D, matComposite);
}
void transformVerts3D()
{
    GLint k;
    for (k = ; k < nVerts; k++){
        resultVerts[k].x = matComposite[][] * verts[k].x + matComposite[][] * verts[k].y + matComposite[][] * verts[k].z + matComposite[][];
        resultVerts[k].y = matComposite[][] * verts[k].x + matComposite[][] * verts[k].y + matComposite[][] * verts[k].z + matComposite[][];
        resultVerts[k].z = matComposite[][] * verts[k].x + matComposite[][] * verts[k].y + matComposite[][] * verts[k].z + matComposite[][];
    }
}

//畫一個三棱錐,每個面設不同的顔色
void draw(pt3D *mat)
{
    int j;
    for (int i = ; i < ; i++) {
        glColor3f(colors[i].r, colors[i].g, colors[i].b);
        for (j = i * ; j < i *  + ; j++) {
            glVertex3f(mat[j].x, mat[j].y, mat[j].z);
        }
    }
}
void displayFunc()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);          
    glRotatef(, , , ); 
    glBegin(GL_LINES);//畫坐标軸,可以省略,主要是為了看旋轉變化
    glColor3f(, , );//y軸紅色
    glVertex3f(, , );
    glVertex3f(, , );
    glColor3f(, , );
    glVertex3f(, , );
    glVertex3f(, , );//x軸綠色
    glColor3f(, , );
    glVertex3f(, , );
    glVertex3f(, , );//z軸藍色
    glEnd();
    glBegin(GL_TRIANGLES);                          // 繪制三角形      
    draw(verts);
    glEnd();
    glLoadIdentity();//不可以省略,否則後面對其它三維物體的變換也會發生在上面已經繪制的物體中
    GLfloat scaleBack =  / scale;
    glRotatef(angle, , , );
    glScalef(, scale, );
    //GLfloat tx = 0, ty = 1.0, tz = 3;
    m4SetIdentity(matComposite);
    translate3D(tx, ty, tz);
    transformVerts3D();
    glBegin(GL_TRIANGLES);     
    draw(resultVerts);
    glEnd();
    glScalef(, scaleBack, );
    glLoadIdentity();
    glFlush();
}
/*
鍵盤設定,下面的還可以改善:當旋轉或平移到一定數值時,回到原始狀态
 */
void processSpecialKeys(int key, int x, int y)
{
    switch (key){
    case GLUT_KEY_UP:ty += ;break;
    case GLUT_KEY_DOWN:ty -= ;break;
    case GLUT_KEY_LEFT:tz += ;break;
    case GLUT_KEY_RIGHT:tz -= ;break;
    case GLUT_KEY_PAGE_UP:scale += ;break;
    case GLUT_KEY_PAGE_DOWN:scale -= ;break;
    case GLUT_KEY_INSERT:angle += ;break;
    case GLUT_KEY_END:tx = ; ty = ; tz = ; angle = ; scale = ; break;//回到原始狀态
    default:break;
    }
    displayFunc();
}
void main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(, );
    glutInitWindowSize(winWidth, winHeight);
    glutCreateWindow("3D");
    init();
    glutDisplayFunc(displayFunc);
    glutSpecialFunc(processSpecialKeys);
    glutMainLoop();
}
           

繼續閱讀