分享

OpenGL利用矩阵对模型进行变换

 贤人好客 2010-12-29

本实例是利用矩阵对模型进行平移、缩放、旋转等变换,虽然实际应用中都有特定的方法,用起来很简单,但此矩阵算法是你理解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,模型会旋转。

OpenGL利用矩阵对模型进行变换 - 随~~枫 - 随枫飘落的思恋————博客

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多