QT与OpenGL之三维旋转
效果:
由于上传大小的限制,只能制作短暂的效果。
在这篇文章中,将要展现的效果是在3D场景中移动位图,得到简单的动画效果。便于大家共同学习,给出全部代码:
在开始前,你应该在pro中添加
LIBS +=glut.lib glut32.lib
同时你也应该将这两个库加到QT的bin目录下。
glwidget.h
#include "glwidget.h"#include "ui_glwidget.h"#include <QtGui>#include <QtCore>#include <QtOpenGL>#include <GL/GLU.h>const int num = 50;typedef struct{ int r, g, b; GLfloat dist; GLfloat angle;}stars;stars star[num];GLfloat light_ambient[4]={0.5, 0.5, 0.5, 1.0};GLfloat light_diffuse[4]={1.0, 1.0, 1.0, 1.0};GLfloat light_position[4]={0.0, 0.0, 2.0, 0.0};GLWidget::GLWidget(QGLWidget *parent) : QGLWidget(parent), ui(new Ui::GLWidget){ ui->setupUi(this); fullscreen = false; rotate_angle = 0.0; zoom = -15.0; title = 90.0; spin = (GLfloat)0.1; loop = 0; twinkle = false; blend = false; startTimer(5);//开启5ms定时器}//这是对虚函数,这里是重写该函数void GLWidget::initializeGL(){ setGeometry(300, 150, 500, 500);//设置窗口初始位置和大小 loadGLTextures(); glEnable(GL_TEXTURE_2D);//允许采用2D纹理技术 glShadeModel(GL_SMOOTH);//设置阴影平滑模式 glClearColor(0.0, 0.0, 0.0, 0.5);//改变窗口的背景颜色 glClearDepth(1.0);//设置深度缓存 glEnable(GL_DEPTH_TEST);//允许深度测试 glDepthFunc(GL_LEQUAL);//设置深度测试类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//进行透视校正 glBlendFunc(GL_SRC_ALPHA, GL_ONE);//源像素因子采用alpha通道值,目标像素因子采用1.0 glEnable(GL_BLEND); /*为num个星星的数组结构体赋初值*/ for(loop = 0; loop < num; loop++) { star[loop].angle = 0.0; star[loop].dist = (float(loop)/num)*5.0;//星星的离中心的距离越来越远,最大距离为5,接近屏幕的距离 star[loop].r = rand()%256; star[loop].g = rand()%256; star[loop].b = rand()%256; }}void GLWidget::paintGL(){ //glClear()函数在这里就是对initializeGL()函数中设置的颜色和缓存深度等起作用 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, texture[0]);//绑定纹理目标 for(loop = 0; loop < num; loop++) { glLoadIdentity(); glTranslatef(0.0, 0.0, zoom);//移向屏幕里面 glRotatef(title, 1.0, 0.0, 0.0);//沿着x轴旋转了tilt度 glRotatef(star[loop].angle, 0.0, 1.0, 0.0);//每个星星沿着y轴旋转自己的角度 glTranslatef(star[loop].dist, 0.0, 0.0); glRotatef(-star[loop].angle, 0.0, 1.0, 0.0);//将沿着y轴旋转的角度又转回去 glRotatef(-title, 1.0, 0.0, 0.0);//将沿着x轴旋转过的角度也转回去 if(twinkle)//如果星星闪烁的话 { glColor4ub(star[num-loop-1].r, star[num-loop-1].g, star[num-loop-1].b, 255);//采用的是对称一头那边的星星的颜色 //将星星的纹理贴到一个小矩形上 glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0); glEnd(); } /*不闪烁时*/ glRotatef(spin, 0.0, 0.0, 1.0);//星星沿z轴自转spin度 glColor4ub(star[loop].r, star[loop].g, star[loop].b, 255);//采用的是自己的颜色,完全不透明 glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0); glEnd(); spin += 0.01f; star[loop].angle += float(loop)/num;//角度是在慢慢增大的 star[loop].dist -= 0.01f;//距离慢慢减小,被吸向了屏幕中心 if(star[loop].dist < 0) { star[loop].dist += 5.0f; star[loop].r = rand()%256; star[loop].g = rand()%256; star[loop].b = rand()%256; } }}//该程序是设置opengl场景透视图,程序中至少被执行一次(程序启动时).void GLWidget::resizeGL(int width, int height){ if(0 == height) height = 1;//防止一条边为0 glViewport(0, 0, (GLint)width, (GLint)height);//重置当前视口,本身不是重置窗口的,只不过是这里被Qt给封装好了 glMatrixMode(GL_PROJECTION);//选择投影矩阵 glLoadIdentity();//重置选择好的投影矩阵 gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);//建立透视投影矩阵 glColor4f(1.0f, 1.0f, 1.0f, 0.5f); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glMatrixMode(GL_MODELVIEW);//以下2句和上面出现的解释一样 glLoadIdentity();}void GLWidget::keyPressEvent(QKeyEvent *e){ switch(e->key()) { /*PageUp键为将木箱移到屏幕内部方向*/ case Qt::Key_T: twinkle = !twinkle; updateGL(); break; /*B键位选择是否采用色彩融合*/ case Qt::Key_B: blend = !blend; if(blend) { glEnable(GL_BLEND);//色彩融合和深度缓存不能同时开启 glDisable(GL_DEPTH_TEST); } else { glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); } updateGL(); break; /*PageUp键为将木箱移到屏幕内部方向*/ case Qt::Key_PageUp: zoom -= 0.2; updateGL(); break; /*PageDown键为将木箱移到屏幕外部方向*/ case Qt::Key_PageDown: zoom += 0.2; updateGL(); break; /*Up键为加快立方体旋转的速度*/ case Qt::Key_Up: title += 0.5; updateGL(); break; /*Down键为减慢立方体旋转的速度*/ case Qt::Key_Down: title -= 0.5; updateGL(); break; /*F1键为全屏和普通屏显示切换键*/ case Qt::Key_F1: fullscreen = !fullscreen; if(fullscreen) showFullScreen(); else { setGeometry(300, 150, 500, 500); showNormal(); } updateGL(); break; /*Ese为退出程序键*/ case Qt::Key_Escape: close(); }}/*装载纹理*/void GLWidget::loadGLTextures(){ QImage tex, buf; if(!buf.load(":/images/2.bmp"))//这个时候因为debug没有在外面,所以图片文件夹就是本目录了 // if(!buf.load("../opengl_qt_nehe_07/crate.bmp")) { qWarning("Cannot open the image..."); QImage dummy(128, 128, QImage::Format_RGB32);//当没找到所需打开的图片时,创建一副128*128大小,深度为32位的位图 dummy.fill(Qt::green); buf = dummy; } tex = convertToGLFormat(buf);//将Qt图片的格式buf转换成opengl的图片格式tex glGenTextures(1, &texture[0]);//开辟3个纹理内存,索引指向texture[0] /*建立第一个纹理*/ glBindTexture(GL_TEXTURE_2D, texture[0]);//将创建的纹理内存指向的内容绑定到纹理对象GL_TEXTURE_2D上,经过这句代码后,以后对 //GL_TEXTURE_2D的操作的任何操作都同时对应与它所绑定的纹理对象 glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());//开始真正创建纹理数据 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//当所显示的纹理比加载进来的纹理小时,采用GL_NEAREST的方法来处理 //GL_NEAREST方式速度非常快,因为它不是真正的滤波,所以占用内存非常 // 小,速度就快了 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//当所显示的纹理比加载进来的纹理大时,采用GL_NEAREST的方法来处理}void GLWidget::timerEvent(QTimerEvent *){ updateGL();}GLWidget::~GLWidget(){ delete ui;}