欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算机图形学-三维物体的平移、旋转、防缩

程序员文章站 2022-07-14 09:50:30
...

要求

  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 = 600, winHeight = 600;
GLint nVerts = 12;//点的个数
GLfloat tx=0, ty=1, tz=3;//存储要平移的距离
GLfloat angle = 60;//旋转角度
GLfloat scale = 2.0;//缩放比例
typedef GLfloat M4[4][4];
M4 matComposite;
class pt3D { public: GLfloat x, y, z; };
pt3D verts[] = {
        { 0.0, 1.0, 0.0 }, { -1.0, -1.0, 1.0 }, { 1.0, -1.0, 1.0 },
        { 0.0, 1.0, 0.0 }, { 1.0, -1.0, 1.0 }, { 1.0, -1.0, -1.0 },
        { 0.0, 1.0, 0.0 }, { 1.0, -1.0, -1.0 }, { -1.0, -1.0, -1.0 },
        { 0.0, 1.0, 0.0 }, { -1.0, -1.0, -1.0 }, { -1.0, -1.0, 1.0 }
};//存放三维物体的各个点坐标,由于有4个三角形面,所以有12个点,设置顶点一致就好,拼接三角形,底面不做绘制
pt3D resultVerts[12];//存放变换后的矩阵,即最新的点坐标
class color { public: GLfloat r, g, b; };
color colors[] = {
        { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 1.0, 1.0, 0.0 }
};//存放每个面的颜色

void init()
{
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glOrtho(-5.0, 5.0, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_PROJECTION);
}
void m4SetIdentity(M4 matIdentity4x4)
{
    GLint col, row;
    for (row = 0; row < 4; row++){
        for (col = 0; col < 4; col++){
            matIdentity4x4[row][col] = (row == col);
        }
    }
}
void m4PreMultiply(M4 m1, M4 m2)
{
    GLint row, col;
    M4 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)
{
    M4 matTranslate3D;
    m4SetIdentity(matTranslate3D);
    matTranslate3D[0][3] = tx;
    matTranslate3D[1][3] = ty;
    matTranslate3D[2][3] = tz;
    m4PreMultiply(matTranslate3D, matComposite);
}
void transformVerts3D()
{
    GLint k;
    for (k = 0; k < nVerts; k++){
        resultVerts[k].x = matComposite[0][0] * verts[k].x + matComposite[0][1] * verts[k].y + matComposite[0][2] * verts[k].z + matComposite[0][3];
        resultVerts[k].y = matComposite[1][0] * verts[k].x + matComposite[1][1] * verts[k].y + matComposite[1][2] * verts[k].z + matComposite[1][3];
        resultVerts[k].z = matComposite[2][0] * verts[k].x + matComposite[2][0] * verts[k].y + matComposite[2][2] * verts[k].z + matComposite[2][3];
    }
}

//画一个三棱锥,每个面设不同的颜色
void draw(pt3D *mat)
{
    int j;
    for (int i = 0; i < 4; i++) {
        glColor3f(colors[i].r, colors[i].g, colors[i].b);
        for (j = i * 3; j < i * 3 + 3; j++) {
            glVertex3f(mat[j].x, mat[j].y, mat[j].z);
        }
    }
}
void displayFunc()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);          
    glRotatef(30, 0.0f, 1.0f, 0.0f); 
    glBegin(GL_LINES);//画坐标轴,可以省略,主要是为了看旋转变化
    glColor3f(1.0, 0.0, 0.0);//y轴红色
    glVertex3f(0, 0, 0);
    glVertex3f(0, 4, 0);
    glColor3f(0.0, 1.0, 0.0);
    glVertex3f(0, 0, 0);
    glVertex3f(4, 0, 0);//x轴绿色
    glColor3f(0.0, 0.0, 1.0);
    glVertex3f(0, 0, 0);
    glVertex3f(0, 0, 3);//z轴蓝色
    glEnd();
    glBegin(GL_TRIANGLES);                          // 绘制三角形      
    draw(verts);
    glEnd();
    glLoadIdentity();//不可以省略,否则后面对其它三维物体的变换也会发生在上面已经绘制的物体中
    GLfloat scaleBack = 1 / scale;
    glRotatef(angle, 0.0, 1.0, 0.0);
    glScalef(1.0, scale, 1.0);
    //GLfloat tx = 0, ty = 1.0, tz = 3;
    m4SetIdentity(matComposite);
    translate3D(tx, ty, tz);
    transformVerts3D();
    glBegin(GL_TRIANGLES);     
    draw(resultVerts);
    glEnd();
    glScalef(1.0, scaleBack, 1.0);
    glLoadIdentity();
    glFlush();
}
/*
键盘设置,下面的还可以改善:当旋转或平移到一定数值时,回到原始状态
 */
void processSpecialKeys(int key, int x, int y)
{
    switch (key){
    case GLUT_KEY_UP:ty += 0.1;break;
    case GLUT_KEY_DOWN:ty -= 0.1;break;
    case GLUT_KEY_LEFT:tz += 0.1;break;
    case GLUT_KEY_RIGHT:tz -= 0.1;break;
    case GLUT_KEY_PAGE_UP:scale += 0.1;break;
    case GLUT_KEY_PAGE_DOWN:scale -= 0.1;break;
    case GLUT_KEY_INSERT:angle += 10;break;
    case GLUT_KEY_END:tx = 0; ty = 1; tz = 3; angle = 60; scale = 2; break;//回到原始状态
    default:break;
    }
    displayFunc();
}
void main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(winWidth, winHeight);
    glutCreateWindow("3D");
    init();
    glutDisplayFunc(displayFunc);
    glutSpecialFunc(processSpecialKeys);
    glutMainLoop();
}