在日常的项目中,我们经常会实现波动的一些纹理效果,比如飘动的旗子,水的波纹,地图上某一点的波浪圈圈等...,本篇介绍波动纹理的实现,旗子的飘动。
在实现波动效果的时候,常用的波动使用的就是正弦函数sin,然后再叠加纹理的实现,就能实现飘动的旗子了。
头文件:
- #include
- #include
- #include
- #include
- #include
- /*
- *旗的效果(波动纹理).
- */
-
- class NeHe_11_Widget : public QGLWidget
- {
- Q_OBJECT
- public:
- NeHe_11_Widget(QWidget* parent = 0);
- ~NeHe_11_Widget();
-
- protected:
- void initializeGL();
- void paintGL();
- void resizeGL( int width, int height );
-
- void loadGLTextures(); //在这个函数中我们会载入指定的图片并生成相应当纹理。
-
- void keyPressEvent( QKeyEvent *e );
- void timerEvent( QTimerEvent *e );
-
- protected:
- GLfloat xRot, yRot, zRot;
- GLfloat hold; //变量hold将存放一个用来对旗形波浪进行光滑的浮点数。
- GLuint texture[1];
-
- float points[45][45][3]; //points数组来存放网格各顶点独立的(x,y,z)坐标.这里网格由45×45点形成,换句话说也就是由44格×44格的小方格子依次组成了。
- int wiggle_count; //wiggle_count用来指定纹理波浪的运动速度,每3帧一次看起来很不错。
- };
cpp文件的实现:
- #include
- #include
-
- #include
- #include
-
- NeHe_11_Widget::NeHe_11_Widget(QWidget *parent):QGLWidget(parent)
- {
- xRot = yRot = zRot = 0.0;
- hold = 0.0;
-
- wiggle_count = 0;
-
- setGeometry( 100, 100, 640, 480 );
-
- startTimer( 50 );
- }
-
-
- NeHe_11_Widget::~NeHe_11_Widget()
- {
- }
-
- void NeHe_11_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我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
-
- glPolygonMode( GL_BACK, GL_FILL );
- glPolygonMode( GL_FRONT, GL_LINE );
- //上面的代码指定使用完全填充模式来填充多边形区域的背面(或者叫做后表面吧)。相反,多边形的正面(前表面)则使用轮廓线填充了。\
- 这些方式完全取决于您的个人喜好。并且与多边形的方位或者顶点的方向有关。详情请参考Red Book。这里我顺便推销一本推动我学习OpenGL\
- 的好书-Addison-Wesley出版的《Programmer's Guide to OpenGL》。个人以为这是学习OpenGL的无价之宝。
-
- for ( int x = 0; x < 45; x++ )
- {
- for ( int y = 0; y < 45; y++ )
- {
- points[x][y][0] = float( ( x/5.0 ) - 4.5 );
- points[x][y][1] = float( ( y/5.0 ) - 4.5 );
- points[x][y][2] = float( sin( ( ( ( x/5.0 ) * 40.0 )/360.0 ) * 3.141592654 * 2.0 ) );
- }
- }
-
- }
这里感谢Graham Gibbons关于使用整数循环变量消除波浪间的脉冲锯齿的建议。
上面的两个循环初始化网格上的点。使用整数循环可以消除由于浮点运算取整造成的脉冲锯齿的出现。我们将x和y变量都除以5,再减去4.5。这样使得我们的波浪可以“居中”(这样计算所得结果将落在区间[-4.5,4.5]之间)。
点[x][y][2]最后的值就是一个sin函数计算的结果。sin()函数需要一个弧度参变量。将float_x乘以40.0,得到角度值。然后除以360.0再乘以PI,乘以2.0,就转换为弧度了。
- void NeHe_11_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_11_Widget::paintGL()
- {
- int x, y;
- float float_x, float_y, float_xb, float_yb;
- //x、y是循环变量,float_x、float_y、float_xb、float_yb是用来将旗形的波浪分割成很小的四边形。
-
- glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
- //清楚屏幕和深度缓存。
-
- glLoadIdentity();
- //重置模型观察矩阵
-
- glTranslatef( 0.0, 0.0, -12.0 );
-
- glRotatef( xRot, 1.0, 0.0, 0.0 );
- glRotatef( yRot, 0.0, 1.0, 0.0 );
- glRotatef( zRot, 0.0, 0.0, 1.0 );
-
- glBindTexture( GL_TEXTURE_2D, texture[0] );
-
-
- glBegin( GL_QUADS );
- for ( x = 0; x < 44; x++ )
- { //沿X平面0-44循环(45点)
- for ( y = 0; y < 44; y++ )
- {
- //沿Y平面0-44循环(45点)
- float_x = float(x)/44.0;
- float_y = float(y)/44.0;
- float_xb = float(x+1)/44.0;
- float_yb = float(y+1)/44.0;
- //上面我们使用4个变量来存放纹理坐标。每个多边形(网格之间的四边形)分别映射了纹理的1/44 x 1/44部分。\
- 循环首先确定左下顶点的值,然后我们据此得到其他三点的值。
-
- glTexCoord2f( float_x, float_y );
- glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] );
-
- glTexCoord2f( float_x, float_yb );
- glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] );
-
- glTexCoord2f( float_xb, float_yb );
- glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] );
-
- glTexCoord2f( float_xb, float_y );
- glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] );
- //上面四个坐标分别为左下、左上、右上、右下。
-
- //上面几行使用glTexCoord2f()和 glVertex3f()载入数据。提醒一点:四边形是逆时针绘制的。这就是说,\
- 您开始所见到的表面是背面。后表面完全填充了,前表面由线条组成。
-
- //如果您按顺时针顺序绘制的话,您初始时见到的可能是前表面。也就是说您将看到网格型的纹理效果而不是完全填充的。
- }
-
- }
- glEnd();
- if ( wiggle_count == 2 )
- {
- for ( y = 0; y < 45; y++ )
- {
- hold = points[0][y][2];
- for ( x = 0; x < 44; x++ )
- {
- points[x][y][2] = points[x+1][y][2];
- }
- points[44][y][2] = hold;
- }
- wiggle_count = 0;
- }
-
- wiggle_count++;
- //上面所作的事情是先存储每一行的第一个值,然后将波浪左移一下,是图象产生波浪。存储的数值挪到末端以产生一个永无尽头\
- 的波浪纹理效果。然后重置计数器 wiggle_count以保持动画的进行。
-
- //上面的代码由NeHe(2000年2月)修改过,以消除波浪间出现的细小锯齿。
-
- xRot += 0.3;
- yRot += 0.2;
- zRot += 0.4;
- //标准的NeHe旋转增量.
-
- }
-
- //loadGLTextures()函数就是用来载入纹理的。
- void NeHe_11_Widget::loadGLTextures()
- {
- QImage tex,buf;
-
- if (!buf.load( ":/data/Tim.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_LINEAR );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
-
- }
-
- //这里就是定时操作函数timerEvent(),执行的操作就是updateGL(),就是刷新窗口了,其实它也会调用paintGL(),所以就实现了每5毫秒刷新一次的动画效果。
- void NeHe_11_Widget::timerEvent(QTimerEvent*)
- {
- updateGL();
- }
-
-
- void NeHe_11_Widget::keyPressEvent( QKeyEvent *e )
- {
- switch ( e->key() )
- {
- case Qt::Key_Escape:
- close();
-
- }
-
- }
正在上面的代码实现后,我们需要再运行看一下效果怎么样。


通过上面的结果展示,我们可以看到旗子飘动的效果,文章所需的资源,我放到了百度网盘,如果链接失效,大家可以私信我。
链接:https://pan.baidu.com/s/1515gb2lUC-NdWQcF60nWNg
提取码:j1lm

上一篇:OpenGL(十八)——Qt OpenGL绘制一个3D世界
下一篇:
本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。