要求
- 利用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 = , 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();
}