计算机图形学-三维物体的平移、旋转、防缩
程序员文章站
2022-07-14 09:50:30
...
要求
- 利用OpenGL函数画一个三维物体;
- 利用鼠标或键盘控制三维物体在屏幕上移动、旋转和放缩
(1)移动:不能直接调用OpenGL几何变换函数,运用齐次坐标,采用矩阵相乘的方式自己编程实现;
(2)旋转、放缩可以调用OpenGL函数
使用工具:
- Visual Studio C++控制台
- opengl
效果图:
实现步骤:
1.基本三维物体绘制:旋转坐标系,用线条表示出x轴、y轴和z轴,然后画出基本的三维物体,调整旋转系数查看三维物体是否绘制成功,如图1所示;
2. 平移功能:运用其次坐标,矩阵相乘及代码,实现平移功能;
3. 旋转和缩放功能:使用OpenGL函数glRotatef和glScalef实现;
4.键盘设置:UP、DOWN、LEFT和RIGHT分别控制物体的上、下、左和右移动,PAGE_UP 和PAGE_DOWN分别控制物体在y轴方向的放大和缩小,INSERT控制物体围绕y轴旋转,END让物体回到初始状态。
知识点:
-
矩阵相乘遵循的规则:
M x N x K = (M X N) x K = M x (N x K)
M x N 不等于 N x M -
平移矩阵设置:
-
旋转矩阵设置:
-
缩放矩阵设置:
部分功能代码:
/* 矩阵初始化,正对角线上设为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();
}