本实例是利用矩阵对模型进行平移、缩放、旋转等变换,虽然实际应用中都有特定的方法,用起来很简单,但此矩阵算法是你理解opengl必须要学会的。也是模型变换的原理。
//包含所需的glut头文件。该头文件已经默认包含gl.h,和glu.h。
#include <glut.h>
//还可以包含其他头文件
#include<math.h> //数学函数的头文件
#include <iostream>
using namespace std;
//定义顶点的个数
#define VERTEX_NUM 3
#define pi 3.14159265
//二维坐标系中的一个点的表示方法
typedef struct
{
GLfloat x;
GLfloat y;
GLfloat h;
}vertex2D;
//三角形的三个顶点的位置坐标
float matrixT[3][3];
vertex2D vOld[VERTEX_NUM] = {{50,50,1},{200,100,1},{150,300,1}};
vertex2D vNew[VERTEX_NUM];
//定义默认的窗口大小常量
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
//定义全局变量,随时保存窗口被改变后,当前窗口的大小
GLsizei gWidth;
GLsizei gHeight;
//为了程序更加清晰,将在整个程序中只需要调用一次的
//用于完成初始化工作的函数在此处调用。
void init()
{
//用于清屏的颜色rgba,在0--1之间的浮点数
glClearColor(0.0, 0.0, 0.0, 0.0);
//指定渲染模式:Flat(整个图元一个颜色), Smooth(光滑,插值)
glShadeModel(GL_FLAT); //默认状态下就是光滑模式
}
//(1)当窗口的内容需要进行重绘时将要被调用的函数。
//如:窗口刚被打开,被弹出,窗口的内容遭到破坏等。
//该函数中定义具体要绘制图形的语句。
void display()
{
cout<<"窗口被重绘"<<endl;
//执行清屏工作。参数表示清除哪个缓存
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0,1,1);
//调用glBegin、glEnd函数绘制图元
glBegin(GL_TRIANGLES); //开始绘制---利用参数指定图元类型为三角形
for(int i=0; i<VERTEX_NUM; i++)
{
//glVertex2f(vNew[i].x, vNew[i].y);
glVertex2f(vOld[i].x, vOld[i].y);
}
glEnd(); //绘制结束
//强制执行opengl函数,不缓存
glFlush();
}
//创建用于执行平移操作的矩阵,
// ty:分别是沿x轴,沿y轴的平移量
void CreateTranslateMatrix(float tx, float ty)
{
//平移矩阵如下
//[1, 0, tx]
//[0, 1, ty]
//[0, 0, 1 ]
matrixT[0][0] = 1;
matrixT[0][1] = 0;
matrixT[0][2] = tx;
matrixT[1][0] = 0;
matrixT[1][1] = 1;
matrixT[1][2] = ty;
matrixT[2][0] = 0;
matrixT[2][1] = 0;
matrixT[2][2] = 1;
}
//等比缩放
// ty:分别是沿x轴,沿y轴的平移量
void sfjz(float tx, float ty)
{
//平移矩阵如下
//[0.8, 0, tx]
//[0, 0.8, ty]
//[0, 0, 0.8 ]
matrixT[0][0] = 0.8;
matrixT[0][1] = 0;
matrixT[0][2] = tx;
matrixT[1][0] = 0;
matrixT[1][1] = 0.8;
matrixT[1][2] = ty;
matrixT[2][0] = 0;
matrixT[2][1] = 0;
matrixT[2][2] = 0.8;
}
void xz()
{
//平移矩阵如下
//[cos(pi/10), -sin(pi/10), 0]
//[sin(pi/10), cos(pi/10), 0]
//[ 0, 0, 1]
matrixT[0][0] = cos(pi/10);
matrixT[0][1] = -sin(pi/10);
matrixT[0][2] = 0;
matrixT[1][0] = sin(pi/10);
matrixT[1][1] = cos(pi/10);
matrixT[1][2] = 0;
matrixT[2][0] = 0;
matrixT[2][1] = 0;
matrixT[2][2] = 1;
}
/**********************实现二维物体的平移操作*****************************/
void TranslateVertex(vertex2D *newPos, float mT[3][3], vertex2D *oldPos)
{
//利用矩阵乘法,将平移矩阵同原始顶点坐标相乘,得到新的坐标点。
for(int i=0; i<3; i++)
{
newPos->x = mT[0][0] * oldPos->x + mT[0][1] * oldPos->y + mT[0][2] * oldPos->h;
newPos->y = mT[1][0] * oldPos->x + mT[1][1] * oldPos->y + mT[1][2] * oldPos->h;
newPos->h = mT[2][0] * oldPos->x + mT[2][1] * oldPos->y + mT[2][2] * oldPos->h;
}
}
void sf(vertex2D *newPos, float mT[3][3], vertex2D *oldPos)
{
//利用矩阵乘法,将平移矩阵同原始顶点坐标相乘,得到新的坐标点。
for(int i=0; i<3; i++)
{
newPos->x = mT[0][0] * oldPos->x + mT[0][1] * oldPos->y + mT[0][2] * oldPos->h;
newPos->y = mT[1][0] * oldPos->x + mT[1][1] * oldPos->y + mT[1][2] * oldPos->h;
newPos->h = mT[2][0] * oldPos->x + mT[2][1] * oldPos->y + mT[2][2] * oldPos->h;
}
}
void DNP() //重绘
{
//清除背景颜色
glClear(GL_COLOR_BUFFER_BIT);
//调用glBegin、glEnd函数将平移后的图形绘制出来
glBegin(GL_TRIANGLES); //开始绘制---利用参数指定图元类型为三角形
for(int i=0; i<VERTEX_NUM; i++)
{
glVertex2f(vNew[i].x, vNew[i].y);
}
glEnd(); //绘制结束
glFlush();
}
//绘制变换过后的图。三个顶点都执行平移操作
void DrawNewPos(float tx, float ty)
{
CreateTranslateMatrix(tx, ty);
//对三个顶点,调用函数进行平移操作。
for(int i=0; i<VERTEX_NUM; i++)
{
TranslateVertex(&(vNew[i]), matrixT, &(vOld[i]));
vOld[i]=vNew[i];
}
DNP();
}
//绘制缩放后的图
void DrawNewPossf(float tx, float ty)
{
sfjz(tx,ty);
//对三个顶点,调用函数进行平移操作。
for(int i=0; i<VERTEX_NUM; i++)
{
TranslateVertex(&(vNew[i]), matrixT, &(vOld[i]));
vOld[i]=vNew[i];
}
DNP();
}
//绘制旋转后的图
void DrawNewPosxz()
{
xz();
//对三个顶点,调用函数进行平移操作。
for(int i=0; i<VERTEX_NUM; i++)
{
TranslateVertex(&(vNew[i]), matrixT, &(vOld[i]));
vOld[i]=vNew[i];
}
DNP();
}
//当屏幕第一次被创建,或窗口的大小改变,窗口被移动的时候调用这个函数
void reshape(int w, int h)
{
//输出被改变的新的窗口大小
cout<<"新的窗口大小:"<<w<<", "<<h<<endl;
//设置新的画布大小,也就是我们看到的窗口的矩形区域大小---视口大小
//视口:是个矩形的窗口区域,图形就是在这个区域中绘制的。
//视口是用窗口坐标来测量的。默认情况下,视口被设置为占据打开窗口的整个像素矩形。
//我们还可以对窗口进行划分,在同一个窗体中显示分隔屏幕的效果。
//参数分别为:窗口左下角,窗口的宽度和高度。
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
//设置投影矩阵---glMatrixMode:指定哪一个矩阵是当前矩阵
glMatrixMode(GL_PROJECTION); //标明接下来的函数影响投影矩阵,而不是模型变换矩阵。
//单位化,消除前面的矩阵留下的影响
glLoadIdentity();
//正投影:参数分别为左右,下上。裁剪区域为矩形。
gluOrtho2D(0.0, 600, 0.0, 400); //定义坐标系范围--视景体
}
//当键盘上的某个键被按下的时候调用这个函数。
void keyboard(unsigned char key, int x, int y)
{
cout<<key<<"键被按下。"<<"鼠标位置为:"<<x<<","<<y<<endl;
float kx=5,ky=5;
//若用户按下t键,对三角形进行平移
switch(key)
{
case (unsigned char)'t':
case (unsigned char)'T':
DrawNewPos(kx,ky);
break;
//等比缩放
case (unsigned char)'m':
case (unsigned char)'M':
DrawNewPossf(kx,ky);
break;
//旋转
case (unsigned char)'x':
case (unsigned char)'X':
DrawNewPosxz();
break;
}
}
void motion(int x, int y)
{
cout<<"鼠标被按下,并移动。 位置为:"<<x<<", "<<y<<endl;
}
int main(int argc, char** argv)
{
//初始化GLUT,在所有glut函数调用之前调用该函数
glutInit(&argc, argv);
//初始化显示模式:RGBA颜色模式, 单缓存
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
//初始化窗口位置
glutInitWindowPosition(0, 0);
//初始化窗口大小
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
//创建上述函数定义的窗口,参数为窗口标题
glutCreateWindow("才才——利用矩阵移动、缩放、旋转模型");
//进行绘图前的一些初始化工作
init();
display();
glutDisplayFunc(display); //设置窗口显示回调函数
glutReshapeFunc(reshape); //设置窗口大小改变回调函数
glutKeyboardFunc(keyboard); //设置键盘回调函数
//glutMouseFunc(mouse); //设置鼠标回调函数
glutMotionFunc(motion); //设置鼠标按住移动回调函数
//glutIdleFunc(idle); //设置空闲回调函数
//进入事件循环,等待事件和消息,直到程序退出为止
glutMainLoop();
return 0;
}
最终效果如下:
当按下T,模型平移;按下M,模型会缩放;按下X,模型会旋转。
