OpenGL(十七)——Qt OpenGL在三维空间移动位图(会动的星星)

一、场景

上一篇介绍了融合的概念,以及要实现融合效果的函数glBlendFunc(),融合,本篇结合之前的融合和物体移动和旋转一起实现旋转的星星。这其中就要用到坐标变换的函数来实现了,在之前的文章中其实也出现了坐标变换的函数。

二、坐标变换函数实现

1、移动到坐标系中心

函数原型:void glLoadIdentity( void);

函数作用:将当前的用户坐标系的原点移到了屏幕中心。类似于一个复位操作 。

注:

1.X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。

2.OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。

3.中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。

2、移动函数

函数原型:void glTranslatef(GLfloat x,GLfloat y,GLfloat z);

函数作用:沿X轴正方向平移x个单位(x是有符号数)
沿Y轴正方向平移y个单位(y是有符号数)
 沿Z轴正方向平移z个单位(z是有符号数)

注:在glTranslatef(x, y, z)中,当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。

其作用就是将你绘点坐标的原点在当前原点的基础上平移一个(x,y,z)向量。

3、旋转函数

函数原型:glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);

函数作用:函数用来设置opengl中绘制实体的自转方式,即物体如何旋转。

参数说明:

angle:旋转的角度,单位为度;

x,y,z表示绕着那个轴旋转,如果取值都为0,则表示默认的绕x轴逆时针旋转。

x,y为0,z不为0时,表示绕z轴旋转;x,z为0,y不为0时,表示绕y轴旋转;y,z为0,x不为0,表示绕x轴旋转。

旋转的逆顺时针是通过x,y,z值得正负来确定的:取值为正时,表示逆时针旋转;取值为负时,表示顺时针旋转。

例如:glRotatef(30,0,-1,0);

表示绕y轴顺时针方向旋转30度。

关于逆时针与顺时针,可用右手定则:

即手握住某个坐标轴,大拇指指向某轴的正方向,其余四个手指的弯曲方向即为绕某轴旋转的逆时针方向;反之为顺时针方向。

三、代码

通过上面的说明,可以看到坐标变换的几种方式,通过这可以实现星星的旋转和移动。

头文件:

#include <QObject>
#include <QWidget>
#include <qgl.h>
#include <QTimer>
#include <QKeyEvent>
#include <QTimerEvent>
/*
*在三维空间中移动位图.
*/typedef struct
{int r, g, b;GLfloat dist;GLfloat angle;
}stars;
//这个结构是用来存储星星的数据的,r、g、b存储的是星星的颜色,dist是星星距离中心的距离,angle是星星现在所在的角度。const GLuint num = 50; //这个常量num存储的是我们绘制的星星的总数。class NeHe_9_Widget : public QGLWidget
{Q_OBJECT
public:NeHe_9_Widget(QWidget *parent = 0);~NeHe_9_Widget();public slots:void slotTimer();protected:void initializeGL(); void paintGL();      void resizeGL( int width, int height ); void loadGLTextures(); void keyPressEvent( QKeyEvent *e );void timerEvent( QTimerEvent *e ); GLfloat xRot, yRot, zRot ; //xRot、yRot、zRot来处理立方体在三个方向上的旋转。//GLuint texture[6];GLfloat zoom;   //zoom是星星距离观察者的距离GLfloat tilt;   //tilt是星星的倾角GLfloat spin;   //spin是闪烁星星的自转GLuint loop;    //loop是用来绘制所有50个星星的全局变量GLuint texture[1]; //texture[1]是用来存储纹理的bool twinkle;  //twinkle是用来表示星星是否闪烁stars star[num]; //star[num]是用来存储50个星星的数据。private:QTimer* m_timer;
};

实现:cpp文件

到现在为止,您应该很好的理解OpenGL了。您已经学会了设置一个OpenGL窗口的每个细节。学会在旋转的物体上贴图并打上光线以及融合(透明)处理。这一课应该算是第一课中级教程。您将学到如下的知识:
在三维场景中移动位图,并去除位图上的黑色象素(使用融合)。接着为黑白纹理上色,最后您将学会创建丰富的色彩,并把上过不同色彩的纹理相互融合,得到简单的动画效果。

#include "nehe_9_widget.h"
#include <GL/glu.h>
#include <GL/gl.h>#include <QImage>
#include <QDebug>NeHe_9_Widget::NeHe_9_Widget(QWidget *parent):QGLWidget(parent)
{xRot = yRot = zRot = 0.0;zoom = -15.0;tilt = 90.0;spin = 0.0;loop = 0;twinkle = false;setGeometry( 100, 200, 640, 480 );startTimer( 5 );m_timer = new QTimer(this);m_timer->setInterval(50);}NeHe_9_Widget::~NeHe_9_Widget()
{
}void NeHe_9_Widget::initializeGL()
{loadGLTextures();//载入纹理.glEnable( GL_TEXTURE_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 );//真正精细的透视修正。这一行告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。glBlendFunc( GL_SRC_ALPHA, GL_ONE );glEnable(GL_BLEND );for ( loop = 0; loop < num; loop++ ){star[loop].angle = 0.0;star[loop].dist = ( float(loop)/num ) * 5.0;star[loop].r = rand() % 256;star[loop].g = rand() % 256;star[loop].b = rand() % 256;}/*设置了每颗星星的起始角度、距离和颜色。您会注意到修改结构的属性有多容易。全部50颗星星都会被循环设置。要改变star[1]的角度我们所要做的只是star[1].angle={某个数值};就这么简单!第loop颗星星离中心的距离是将loop的值除以星星的总颗数,然后乘上5.0。基本上这样使得后一颗星星比前一颗星星离中心更远一点。这样当loop为50时(最后一颗星星),loop除以num正好是1.0。之所以要乘以5.0是因为1.0*5.0就是5.0。5.0已经很接近屏幕边缘。我不想星星飞出屏幕,5.0是最好的选择了。当然如果如果您将场景设置的更深入屏幕里面的话,也许可以使用大于5.0的数值,但星星看起来就更小一些(都是透视的缘故)。您还会注意到每颗星星的颜色都是从0~255之间的一个随机数。也许您会奇怪为何这里的颜色得取值范围不是OpenGL通常的0.0~1.0之间。这里我们使用的颜色设置函数是glColor4ub,而不是以前的glColor4f。ub意味着参数是unsigned byte型的。一个byte的取值范围是0~255。这里使用byte值取随机整数似乎要比取一个浮点的随机数更容易一些。*/
}void NeHe_9_Widget::resizeGL( int width, int height )
{if ( height == 0 ){height = 1;}//防止height为0。glViewport( 0, 0, (GLint)width, (GLint)height );//重置当前的视口(Viewport)。glMatrixMode( GL_PROJECTION );//选择投影矩阵。glLoadIdentity();//重置投影矩阵。gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );//建立透视投影矩阵。glMatrixMode( GL_MODELVIEW );//选择模型观察矩阵。glLoadIdentity();//重置模型观察矩阵。}void NeHe_9_Widget::paintGL()
{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( tilt, 1.0, 0.0, 0.0 );//倾斜视角(使用“tilt”的值)。glRotatef( star[loop].angle, 0.0, 1.0, 0.0 );//接下来的代码,我们来移动星星。星星开始时位于屏幕的中心。我们要做的第一件事是把场景沿Y轴旋转。\如果我们旋转90度的话,X轴不再是自左至右的了,他将由里向外穿出屏幕。为了让大家更清楚些,举个例子。\假想您站在房子中间。再设想您左侧的墙上写着-x,前面的墙上写着-z,右面墙上就是+x咯,您身后的墙上则是+z。\加入整个房子向右转90度,但您没有动,那么前面的墙上将是-x而不再是-z了。所有其他的墙也都跟着移动。-z出现\在右侧,+z出现在左侧,+x出现在您背后。神经错乱了吧?通过旋转场景,我们改变了x和z平面的方向。glTranslatef( star[loop].dist, 0.0, 0.0 );//这代码沿x轴移动一个正值。通常x轴上的正值代表移向了屏幕的右侧(也就是通常的x轴的正向),但这里由于我们\绕y轴旋转了坐标系,x轴的正向可以是任意方向。如果我们转180度的话,屏幕的左右侧就镜像反向了。因此,当我们\沿x轴正向移动时,可能向左,向右,向前或向后。glRotatef( -star[loop].angle, 0.0, 1.0, 0.0 );glRotatef( -tilt, 1.0, 0.0, 0.0 );//星星实际上是一个平面的纹理。现在您在屏幕中心画了个平面的四边形然后贴上纹理,这看起来很不错。一切都如您所\想的那样。但是当您当您沿着y轴转上个90度的话,纹理在屏幕上就只剩右侧和左侧的两条边朝着您。看起来就是一条细线。\这不是我们所想要的。我们希望星星永远正面朝着我们,而不管屏幕如何旋转或倾斜。//我们通过在绘制星星之前,抵消对星星所作的任何旋转来实现这个愿望。您可以采用逆序来抵消旋转。当我们倾斜屏幕时,\我们实际上以当前角度旋转了星星。通过逆序,我们又以当前角度“反旋转”星星。也就是以当前角度的负值来旋转星星。就是\说,如果我们将星星旋转了10度的话,又将其旋转-10度来使星星在那个轴上重新面对屏幕。上面的第一行抵消了沿y轴的旋转。\然后,我们还需要抵消掉沿x轴的屏幕倾斜。要做到这一点,我们只需要将屏幕再旋转-tilt倾角。在抵消掉x和y轴的旋转后,\星星又完全面对着我们了。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();}//如果twinkle为真,我们在屏幕上先画一次不旋转的星星:将星星总数(num)减去当前的星星数(loop)再减去1,来提取每\颗星星的不同颜色(这么做是因为循环范围从0到num-1)。举例来说,结果为10的时候,我们就使用10号星星的颜色。这样相邻星星\的颜色总是不同的。这不是个好法子,但很有效。最后一个值是alpha通道分量。这个值越小,这颗星星就越暗。//由于启用了twinkle,每颗星星最后会被绘制两遍。程序运行起来会慢一些,这要看您的机器性能如何了。但两遍绘制的星星颜色\相互融合,会产生很棒的效果。同时由于第一遍的星星没有旋转,启用twinkle后的星星看起来有一种动画效果。(如果您这里看不懂\得话,就自己去看程序的运行效果吧。)//值得注意的是给纹理上色是件很容易的事。尽管纹理本身是黑白的,纹理将变成我们在绘制它之前选定的任意颜色。此外,同样值得\注意的是我们在这里使用的颜色值是byte型的,而不是通常的浮点数。甚至alpha通道分量也是如此。glRotatef( spin, 0.0, 0.0, 1.0 );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();//上面是绘制第二遍的星星。唯一和前面的代码不同的是这一遍的星星肯定会被绘制,并且这次的星星绕着z轴旋转。spin += 0.01;star[loop].angle += float(loop)/num;star[loop].dist -= 0.01;//以上的代码代表星星的运动。我们增加spin的值来旋转所有的星星(公转)。然后,将每颗星星的自转角度增加loop/num。\这使离中心更远的星星转的更快。最后减少每颗星星离屏幕中心的距离。这样看起来,星星们好像被不断地吸入屏幕的中心。if ( star[loop].dist < 0.0 ){star[loop].dist += 5.0;star[loop].r = rand() % 256;star[loop].g = rand() % 256;star[loop].b = rand() % 256;}//检查星星是否已经碰到了屏幕中心。当星星碰到屏幕中心时,我们为它赋一个新颜色,然后往外移5个单位,这颗星星将踏上它回归屏幕中心的旅程。}
}//loadGLTextures()函数就是用来载入纹理的。
void NeHe_9_Widget::loadGLTextures()
{QImage tex,buf;if (!buf.load( ":/data/Star.bmp" )){qWarning( "Could not read image file, using single-color instead." );QImage dummy( 128, 128, QImage::Format_RGB32 );dummy.fill( Qt::green );buf = dummy;//如果载入不成功,自动生成一个128*128的32位色的绿色图片。}tex = QGLWidget::convertToGLFormat(buf);glGenTextures( 1, &texture[0] );glBindTexture( GL_TEXTURE_2D, texture[0] );glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );}void NeHe_9_Widget::slotTimer()
{xRot += 0.3;yRot += 0.2;zRot += 0.4;update();
}//这里就是定时操作函数timerEvent(),执行的操作就是updateGL(),就是刷新窗口了,其实它也会调用paintGL(),所以就实现了每5毫秒刷新一次的动画效果。
void NeHe_9_Widget::timerEvent(QTimerEvent*)
{updateGL();
}void NeHe_9_Widget::keyPressEvent( QKeyEvent *e )
{switch ( e->key() ){case Qt::Key_T: //按下了T键,就可以切换是否闪烁。twinkle = !twinkle;updateGL();break;case Qt::Key_PageUp: //按下了PageUp键,将木箱移向屏幕内部。zoom -= 0.2;updateGL();break;case Qt::Key_PageDown: //按下了PageDown键,将木箱移向屏幕外部。zoom += 0.2;updateGL();break;case Qt::Key_W: //按下了Up方向键,减少xSpeed。tilt -= 0.5;updateGL();break;case Qt::Key_S: //按下了Dowm方向键,增加xSpeed。tilt += 0.5;updateGL();break;case Qt::Key_Escape:close();}}

这一章来解释如何加载一个灰阶位图纹理,(使用融合)去掉它的背景色后,再给它上色,最后让它在三维场景中移动。
展示了如何创建漂亮的颜色与动画效果。实现原理是在原始位图上再重叠一份位图拷贝。到现在为止,只要您很好的理解了我所教您的一切,您应该已经能够毫无问题的制作您自己的3D Demo了。所有的基础知识都已包括在内!

四、运行

正在上面的代码实现后,我们需要再运行看一下效果怎么样。

1、运行

2、是否闪烁

3、放大和缩小

五、资料

通过上面的结果展示,我们可以看到旋转的星星的效果,文章所需的资源,我放到了百度网盘,如果链接失效,大家可以私信我。

链接:https://pan.baidu.com/s/1yg290Exbb4iUlNLTHZmTwQ 
提取码:2agn

上一篇:OpenGL(十六)——Qt OpenGL融合(将两张图片叠合成一张图片)

下一篇:OpenGL(十八)——Qt OpenGL绘制一个3D世界

本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。

OpenGL(十七)——Qt OpenGL在三维空间移动位图(会动的星星)相关推荐

  1. OpenGL(十六)——Qt OpenGL融合(将两张图片叠合成一张图片)

    OpenGL(十六)--Qt OpenGL融合(将两张图片叠合成一张图片) 一.场景 在常用的项目场景中,我们经常会遇到将两个图片合在一起变成一张图片,这时候就会有前后之分,特别是两个物体合在一起的时 ...

  2. OpenGL(十八)——Qt OpenGL绘制一个3D世界

    OpenGL(十八)--Qt OpenGL绘制一个3D世界 一.说明 本篇介绍构建一个3D的世界. 二.简介 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游. ...

  3. Qt OpenGL(二十七)——Qt OpenGL 核心模式--EBO

    Qt OpenGL(二十七)--Qt OpenGL 核心模式--EBO 一.四边形 上篇文章,一川绘制了能旋转的彩色三角形,在之前也介绍了VAO.VBO.但是EBO这个概念没有介绍,现在经历了,前几篇 ...

  4. OpenGL(十四)——Qt OpenGL纹理

    OpenGL(十四)--Qt OpenGL纹理 一.纹理 终于写到纹理的部分了: 纹理(Texture)的本质是一个2D图片(1D和3D),或者叫图形数据.只是在OpenGL中专业术语中称其为纹理. ...

  5. Qt OpenGL 蒙板

    这次教程中,我们教介绍OpenGL的蒙板技术.到目前为止,我们已经学会如何使用alpha混合,把一个透明物体渲染到屏幕上了,但有时使用它看起来并不是那么的复合我们的心意.使用蒙板技术,将会使图像按照我 ...

  6. Qt OpenGL(二十八)——Qt OpenGL 核心模式-绘制一个正方体(正六面体)

    Qt OpenGL(二十七)--Qt OpenGL 核心模式-绘制一个正方体(正六面体) 截止到上一篇文章,一川想使用Qt封装的类绘制一个旋转的彩色三角形的目标就实现了. 上一篇在介绍了使用变换矩阵的 ...

  7. QT openGL 编程 笔记

    据说QT牛,特地花了一个晚上来研究研究.其实我也不想把自己搞得熊猫眼,只是碰到问题了精力就特好,想睡觉都不行.第一次尝试在LINUX下用OpenGL编程,实在是很兴奋,只是感觉碰了不少灰.一个问题从1 ...

  8. linux上qt配置opengl,Ubuntu下配置Qt+OpenGL+OpenCV

    我的平台:Ubuntu 10.04+Qt+OpenGL+OpenCV OpenGL是Qt自带的,Windows下的版本OpenGL库已经随同Qt的Windows发行版一同安装,Linux版本则没有安装 ...

  9. 【eoe特刊】第二十七期 OpenGL ES学习及项目解析

    经过一个月征稿.编辑,新的一版特刊终于出炉了. 本次特刊的制作,改变以往的制作方式,完全取自网友的独自的风格. 在只有一个主题的前提下,完全是通过社区的热心的网友,根据自己的想法,自行设计,自由发挥, ...

  10. QT+OpenGL中glTexImage2D不生效问题

    QT+opengl开发,生成texture并赋值如下 glGenTextures(1,&m_inputTexture); glBindTexture(GL_TEXTURE_2D, m_inpu ...

最新文章

  1. cygwin执行.py提示找不到模块,但已经安装模块的解决办法
  2. 【转】Jmeter常见问题
  3. “半真半假”DeepFake换脸也能精准识别?阿里安全提出全新检测方法
  4. Apache伪静态学习
  5. P4719-[模板]动态DP【矩阵乘法,树链剖分,线段树】
  6. 这几道 Redis 面试题都不懂,怎么拿 Offer?
  7. 【BZOJ3589】动态树
  8. 前端学习(1657):前端系列实战课程之文字输入框实现思路
  9. linux传输tcp命令,Linux tcpdump命令帮助和示例
  10. 窗体控件常用属性 1217
  11. 【elasticsearch系列】安装elasticsearch-head插件
  12. oracle左右结合,oracle中的左右连接
  13. CSRF与XSS攻击的原理与防范
  14. 小程序积分商城如何实现营销目的
  15. JS 如何实现获取本地文件夹名称
  16. MAC wps中选中的页面和缩放打印
  17. IC卡管理系统(Java基础)
  18. 【水文】2345——卸载!拜拜了您嘞
  19. windows如何截屏
  20. win7下搭载ubuntu双系统,独立引导

热门文章

  1. 如果兔子拼命奔跑 乌龟怎么办
  2. 浙大计算机学院12月12日毅行,感谢参与lt;2020年秋季浙大飘渺毅行gt;
  3. 文学类的论文真的不需要数据么?
  4. 群面时被问到“让你淘汰一个组员”一般如何淘汰谁?
  5. CAN、485隔离电路分享
  6. ckplayer html播放本地,vue中使用ckplayer播放器
  7. 免费开通量化软件恒生ptrade和讯投qmt
  8. PTA-查询水果价格
  9. 计算机蓝屏无法启动不了系统怎么办,电脑开机蓝屏死机,无法进入系统怎么办?...
  10. 神舟笔记本键盘灯不亮驱动