實驗四:(2學時)
一、 實驗目的:
掌握幾何變換的原理,尤其是複合變換
二、 實驗內容:
1、利用OpenGL函數畫一個三維物體;
2、運用齊次座標,採用矩陣相乘的方式自己編程實現幾何變換,不能直接調用OpenGL幾何變換函數;
3、利用鼠標或鍵盤控制三維物體在屏幕上移動、旋轉和放縮;
三、 實現效果及步驟(或流程)
1、利用OpenGL函數畫一個三維物體;
實現方法:
(1)初始化時給八個座標,座標之間兩兩相連畫線,代碼如下:
// 將立方體的八個頂點保存到一個數組裏面
static float vertex_list[][3] =
{
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};
// 將要使用的頂點的序號保存到一個數組裏面
static const GLint index_list[][2] =
{
{ 0, 1 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
{ 0, 2 },
{ 1, 3 },
{ 4, 6 },
{ 5, 7 },
{ 0, 4 },
{ 1, 5 },
{ 7, 3 },
{ 2, 6 }
};
//繪製立方體
void DrawCube(void)
{
int i, j;
glBegin(GL_LINES);
for (i = 0; i < 12; ++i) // 12 條線段
{
for (j = 0; j < 2; ++j) // 每條線段 2個頂點
{
glVertex3fv(vertex_list[index_list[i][j]]);
}
}
glEnd();
}
2、運用齊次座標,採用矩陣相乘的方式自己編程實現幾何變換,不能直接調用OpenGL幾何變換函數;
實現方法:
(1)移動函數:
typedef GLfloat Matrix4x4[4][4];
void matrix4x4SetIdentity(Matrix4x4 matIdent4x4) {
GLint row, col;
for (row = 0; row < 4; row++)
for (col = 0; col < 4; col++) matIdent4x4[row][col] = (row == col);
}
//自定義平移函數
void translate3D(GLfloat tx, GLfloat ty, GLfloat tz) {
Matrix4x4 matTransl3D;
matrix4x4SetIdentity(matTransl3D);
matTransl3D[0][3] = tx;
matTransl3D[1][3] = ty;
matTransl3D[2][3] = tz;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 3; j++)
{
vertex_list[i][j] += matTransl3D[j][3];
}
}
}
(2)旋轉函數:
typedef GLfloat Matrix4x4[4][4];
void matrix4x4SetIdentity(Matrix4x4 matIdent4x4) {
GLint row, col;
for (row = 0; row < 4; row++)
for (col = 0; col < 4; col++) matIdent4x4[row][col] = (row == col);
}
//自定義旋轉函數
void rotate3D(GLfloat e, GLfloat rx, GLfloat ry, GLfloat rz)
{
Matrix4x4 matRotate3D;
matrix4x4SetIdentity(matRotate3D);
if (rx == 1 && ry == 0 && rz == 0)
{
matRotate3D[0][0] = 1;
matRotate3D[1][1] = cos(e);
matRotate3D[1][2] = sin(e);
matRotate3D[2][1] = -sin(e);
matRotate3D[2][2] = cos(e);
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0];
vertex_list[i][1] = matRotate3D[1][1] * vertex_list[i][1] + matRotate3D[1][2] * vertex_list[i][2];
vertex_list[i][2] = matRotate3D[2][1] * vertex_list[i][1] + matRotate3D[2][2] * vertex_list[i][2];
}
}
else if (rx == 0 && ry == 1 && rz == 0)
{
matRotate3D[0][0] = cos(e);
matRotate3D[0][2] = -sin(e);
matRotate3D[1][1] = 1;
matRotate3D[2][0] = sin(e);
matRotate3D[2][2] = cos(e);
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0] + matRotate3D[0][2] * vertex_list[i][2];
vertex_list[i][1] = matRotate3D[1][1] * vertex_list[i][1];
vertex_list[i][2] = matRotate3D[2][0] * vertex_list[i][0] + matRotate3D[2][2] * vertex_list[i][2];
}
}
else if (rx == 0 && ry == 0 && rz == 1)
{
matRotate3D[0][0] = cos(e);
matRotate3D[0][1] = -sin(e);
matRotate3D[1][0] = sin(e);
matRotate3D[1][1] = cos(e);
matRotate3D[2][2] = 1;
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0] + matRotate3D[0][1] * vertex_list[i][1];
vertex_list[i][1] = matRotate3D[1][0] * vertex_list[i][0] + matRotate3D[1][1] * vertex_list[i][1];
vertex_list[i][2] = matRotate3D[2][2] * vertex_list[i][2];
}
}
}
(3)縮放函數:
wcPt3D fixedPt = wcPt3D();
typedef GLfloat Matrix4x4[4][4];
void matrix4x4SetIdentity(Matrix4x4 matIdent4x4) {
GLint row, col;
for (row = 0; row < 4; row++)
for (col = 0; col < 4; col++) matIdent4x4[row][col] = (row == col);
}
//自定義縮放函數
void scale3D(GLfloat sx, GLfloat sy, GLfloat sz, wcPt3D fixedPt) {
Matrix4x4 matScale3D;
matrix4x4SetIdentity(matScale3D);
matScale3D[0][0] = sx;
matScale3D[0][3] = (1 - sx) * fixedPt.getx();
matScale3D[1][1] = sy;
matScale3D[1][3] = (1 - sy) * fixedPt.gety();
matScale3D[2][2] = sz;
matScale3D[2][3] = (1 - sz) * fixedPt.getz();
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matScale3D[0][0] * vertex_list[i][0] + matScale3D[0][3] * vertex_list[i][3];
vertex_list[i][1] = matScale3D[1][1] * vertex_list[i][1] + matScale3D[1][3] * vertex_list[i][3];
vertex_list[i][2] = matScale3D[2][2] * vertex_list[i][2] + matScale3D[2][3] * vertex_list[i][3];
}
}
3、利用鼠標或鍵盤控制三維物體在屏幕上移動、旋轉和放縮;
實現效果:
(1)鼠標左鍵拖拽可以移動圖形。
(2)鼠標右鍵拖拽可以旋轉圖形。
(3)鼠標滾輪滾動可以縮放圖形
實現代碼如下:
void OnMouseMove(int x, int y)
{
printf("x:%d,y:%d\n", x, y);
if (LeftButtonIsDown == 1)
{
x1 = x;
y11 = 500 - y;
tx = x1 - x0;
ty = y11 - y00;
tz = 0;
}
if (RightButtonIsDown == 1)
{
x3 = x;
y3 = 500 - y;
rx = x3 - x2;
ry = y3 - y2;
rz = 0;
}
}
void MouseFunc(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
if (LeftButtonIsDown == 0)
{
x0 = x;
y00 = 500 - y;
LeftButtonIsDown = 1;
}
}
else
{
LeftButtonIsDown = 0;
}
}
if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
{
if (RightButtonIsDown == 0)
{
x2 = x;
y2 = 500 - y;
RightButtonIsDown = 1;
}
}
else
{
RightButtonIsDown = 0;
}
}
if (button == GLUT_WHEEL_UP)
{
zoom += 0.01;
}
if (button == GLUT_WHEEL_DOWN)
{
zoom -= 0.01;
}
}
完整的項目源碼如下:
// test4.cpp : 定義控制檯應用程序的入口點。
#include "stdafx.h"
#include<GL/glut.h>
#include<math.h>
#define GLUT_WHEEL_UP 3
#define GLUT_WHEEL_DOWN 4
// 繪製立方體
//參數
static float rotate = 0.001;
static float translate = 0.0001;
static float PI = 3.14159;
//鼠標按下時的座標
int x0, y00;
//鼠標停下時的座標
int x1, y11;
//鼠標是否按下
int LeftButtonIsDown = 0;
//鼠標按下時的座標
int x2, y2;
//鼠標停下時的座標
int x3, y3;
//鼠標是否按下
int RightButtonIsDown = 0;
//位移增量
float tx = 0;
float ty = 0;
float tz = 0;
//旋轉增量
float rx = 0;
float ry = 0;
float rz = 0;
//縮放增量
float zoom = 1;
// 將立方體的八個頂點保存到一個數組裏面
static float vertex_list[][3] =
{
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};
// 將要使用的頂點的序號保存到一個數組裏面
static const GLint index_list[][2] =
{
{ 0, 1 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
{ 0, 2 },
{ 1, 3 },
{ 4, 6 },
{ 5, 7 },
{ 0, 4 },
{ 1, 5 },
{ 7, 3 },
{ 2, 6 }
};
class wcPt3D {
private:
GLfloat x, y, z;
public:
wcPt3D() {
x = y = z = 0.0;
}
void setCoords(GLfloat xCoord, GLfloat yCoord, GLfloat zCoord) {
x = xCoord;
y = yCoord;
z = zCoord;
}
GLfloat getx() const { return x; }
GLfloat gety() const { return y; }
GLfloat getz() const { return z; }
};
wcPt3D fixedPt = wcPt3D();
typedef GLfloat Matrix4x4[4][4];
void matrix4x4SetIdentity(Matrix4x4 matIdent4x4) {
GLint row, col;
for (row = 0; row < 4; row++)
for (col = 0; col < 4; col++) matIdent4x4[row][col] = (row == col);
}
//自定義平移函數
void translate3D(GLfloat tx, GLfloat ty, GLfloat tz) {
Matrix4x4 matTransl3D;
matrix4x4SetIdentity(matTransl3D);
matTransl3D[0][3] = tx;
matTransl3D[1][3] = ty;
matTransl3D[2][3] = tz;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 3; j++)
{
vertex_list[i][j] += matTransl3D[j][3];
}
}
}
//自定義旋轉函數
void rotate3D(GLfloat e, GLfloat rx, GLfloat ry, GLfloat rz)
{
Matrix4x4 matRotate3D;
matrix4x4SetIdentity(matRotate3D);
if (rx == 1 && ry == 0 && rz == 0)
{
matRotate3D[0][0] = 1;
matRotate3D[1][1] = cos(e);
matRotate3D[1][2] = sin(e);
matRotate3D[2][1] = -sin(e);
matRotate3D[2][2] = cos(e);
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0];
vertex_list[i][1] = matRotate3D[1][1] * vertex_list[i][1] + matRotate3D[1][2] * vertex_list[i][2];
vertex_list[i][2] = matRotate3D[2][1] * vertex_list[i][1] + matRotate3D[2][2] * vertex_list[i][2];
}
}
else if (rx == 0 && ry == 1 && rz == 0)
{
matRotate3D[0][0] = cos(e);
matRotate3D[0][2] = -sin(e);
matRotate3D[1][1] = 1;
matRotate3D[2][0] = sin(e);
matRotate3D[2][2] = cos(e);
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0] + matRotate3D[0][2] * vertex_list[i][2];
vertex_list[i][1] = matRotate3D[1][1] * vertex_list[i][1];
vertex_list[i][2] = matRotate3D[2][0] * vertex_list[i][0] + matRotate3D[2][2] * vertex_list[i][2];
}
}
else if (rx == 0 && ry == 0 && rz == 1)
{
matRotate3D[0][0] = cos(e);
matRotate3D[0][1] = -sin(e);
matRotate3D[1][0] = sin(e);
matRotate3D[1][1] = cos(e);
matRotate3D[2][2] = 1;
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matRotate3D[0][0] * vertex_list[i][0] + matRotate3D[0][1] * vertex_list[i][1];
vertex_list[i][1] = matRotate3D[1][0] * vertex_list[i][0] + matRotate3D[1][1] * vertex_list[i][1];
vertex_list[i][2] = matRotate3D[2][2] * vertex_list[i][2];
}
}
//double e = (180 * a) / PI;
}
//自定義縮放函數
void scale3D(GLfloat sx, GLfloat sy, GLfloat sz, wcPt3D fixedPt) {
Matrix4x4 matScale3D;
matrix4x4SetIdentity(matScale3D);
matScale3D[0][0] = sx;
matScale3D[0][3] = (1 - sx) * fixedPt.getx();
matScale3D[1][1] = sy;
matScale3D[1][3] = (1 - sy) * fixedPt.gety();
matScale3D[2][2] = sz;
matScale3D[2][3] = (1 - sz) * fixedPt.getz();
for (int i = 0; i < 8; i++)
{
vertex_list[i][0] = matScale3D[0][0] * vertex_list[i][0] + matScale3D[0][3] * vertex_list[i][3];
vertex_list[i][1] = matScale3D[1][1] * vertex_list[i][1] + matScale3D[1][3] * vertex_list[i][3];
vertex_list[i][2] = matScale3D[2][2] * vertex_list[i][2] + matScale3D[2][3] * vertex_list[i][3];
}
}
//繪製立方體
void DrawCube(void)
{
int i, j;
glBegin(GL_LINES);
for (i = 0; i < 12; ++i) // 12 條線段
{
for (j = 0; j < 2; ++j) // 每條線段 2個頂點
{
glVertex3fv(vertex_list[index_list[i][j]]);
}
}
glEnd();
}
void renderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
//獲取當前矩陣的單位矩陣,以後變化都基於這個座標。
glLoadIdentity();
//將當前矩陣壓入棧內。
glPushMatrix();
//平移
translate3D((tx)*translate, (ty)*translate, 0);
//旋轉
rotate3D(-rx*rotate, 0, 1, 0);
rotate3D(ry*rotate, 1, 0, 0);
// 縮放
scale3D(zoom, zoom, zoom, fixedPt);
//畫筆顏色
glColor3f(1, 1, 1);
//畫立方體
DrawCube();
//輸出棧頂的矩陣
glPopMatrix();
//雙緩衝交換
glutSwapBuffers();
//移動變量初始化
tx = 0;
ty = 0;
tz = 0;
//旋轉變量初始化
rx = 0;
ry = 0;
rz = 0;
//縮放變量初始化
zoom = 1;
}
void OnMouseMove(int x, int y)
{
printf("x:%d,y:%d\n", x, y);
if (LeftButtonIsDown == 1)
{
x1 = x;
y11 = 500 - y;
tx = x1 - x0;
ty = y11 - y00;
tz = 0;
}
if (RightButtonIsDown == 1)
{
x3 = x;
y3 = 500 - y;
rx = x3 - x2;
ry = y3 - y2;
rz = 0;
}
}
void MouseFunc(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
if (LeftButtonIsDown == 0)
{
x0 = x;
y00 = 500 - y;
LeftButtonIsDown = 1;
}
}
else
{
LeftButtonIsDown = 0;
}
}
if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
{
if (RightButtonIsDown == 0)
{
x2 = x;
y2 = 500 - y;
RightButtonIsDown = 1;
}
}
else
{
RightButtonIsDown = 0;
}
}
if (button == GLUT_WHEEL_UP)
{
zoom += 0.01;
}
if (button == GLUT_WHEEL_DOWN)
{
zoom -= 0.01;
}
}
void init()
{
DrawCube();
}
void main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(500, 500);
glutCreateWindow("GLDemo");
init();
glutMouseFunc(MouseFunc);
glutMotionFunc(OnMouseMove);
//func:在程序空閒的時候就會被調用的函數的函數名。
glutIdleFunc(renderScene);
glutMainLoop();
}