Qt5版NeHe OpenGL教程之十:飘动的旗帜
扫描二维码
随时随地手机看文章
这一课将把如下图片做成一个飘动的旗帜,其实主要还是用到了纹理映射。
lesson10.h
#ifndef LESSON10_H #define LESSON10_H #include#include#includeclass QPainter; class QOpenGLContext; class QOpenGLPaintDevice; class Lesson10 : public QWindow, QOpenGLFunctions_1_1 { Q_OBJECT public: explicit Lesson10(QWindow *parent = 0); ~Lesson10(); virtual void render(QPainter *); virtual void render(); virtual void initialize(); public slots: void renderNow(); protected: void exposeEvent(QExposeEvent *); void resizeEvent(QResizeEvent *); void keyPressEvent(QKeyEvent *); // 键盘事件 void timerEvent(QTimerEvent *); // 定时器 private: void loadGLTexture(); private: QOpenGLContext *m_context; GLfloat m_x_rotate; GLfloat m_y_rotate; GLfloat m_z_rotate; GLuint m_texture[1]; //我们将使用points数组来存放网格各顶点独立的x,y,z坐标。这里网格由45×45点形成, //换句话说也就是由44格×44格的小方格子依次组成了。 float m_points[45][45][3]; // Points网格顶点数组 }; #endif // LESSON10_H
lessson10.cpp
#include "lesson10.h" #include#include#include#include#include#includeLesson10::Lesson10(QWindow *parent) : QWindow(parent) , m_context(0) , m_x_rotate(0.0f) , m_y_rotate(0.0f) , m_z_rotate(0.0f) { setSurfaceType(QWindow::OpenGLSurface); startTimer(20); } Lesson10::~Lesson10() { glDeleteTextures(1, &m_texture[0]); } void Lesson10::render(QPainter *painter) { Q_UNUSED(painter); } void Lesson10::render() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glViewport(0,0,(GLint)width(),(GLint)height()); // 重置当前视口 glMatrixMode(GL_PROJECTION); // 选择投影矩阵 glLoadIdentity(); // 重置投影矩阵为单位矩阵 gluPerspective(45.0f,(GLdouble)width()/(GLdouble)height(),0.1f,100.0f); glMatrixMode(GL_MODELVIEW); // 选择模型视图矩阵 glLoadIdentity(); // 重置模型视图矩阵为单位矩阵 float float_x, float_y, float_xb, float_yb; // 用来将旗形的波浪分割成很小的四边形 glTranslatef(0.0f,0.0f,-12.0f); // 移入屏幕12个单位 glRotatef(m_x_rotate,1.0f,0.0f,0.0f); // 绕 X 轴旋转 glRotatef(m_y_rotate,0.0f,1.0f,0.0f); // 绕 Y 轴旋转 glRotatef(m_z_rotate,0.0f,0.0f,1.0f); // 绕 Z 轴旋转 glBindTexture(GL_TEXTURE_2D, m_texture[0]); // 选择纹理 glBegin(GL_QUADS); // 四边形绘制开始 for(int x = 0; x < 44; x++ ) // 沿X平面0-44循环(45点) { for(int y = 0; y < 44; y++ ) // 沿Y平面0-44循环(45点) { //接着开始使用循环进行多边形绘制。这里使用整型可以避免我以前所用的int()强制类型转换。 float_x = float(x)/44.0f; // 生成X浮点值 float_y = float(y)/44.0f; // 生成Y浮点值 float_xb = float(x+1)/44.0f; // X浮点值+0.0227f float_yb = float(y+1)/44.0f; // Y浮点值+0.0227f //上面我们使用4个变量来存放纹理坐标。每个多边形(网格之间的四边形)分别映射了纹理的1/44×1/44部分。 //循环首先确定左下顶点的值,然后我们据此得到其他三点的值。 glTexCoord2f( float_x, float_y); // 第一个纹理坐标 (左下角) glVertex3f( m_points[x][y][0], m_points[x][y][1], m_points[x][y][2] ); glTexCoord2f( float_x, float_yb ); // 第二个纹理坐标 (左上角) glVertex3f( m_points[x][y+1][0], m_points[x][y+1][1], m_points[x][y+1][2] ); glTexCoord2f( float_xb, float_yb ); // 第三个纹理坐标 (右上角) glVertex3f( m_points[x+1][y+1][0], m_points[x+1][y+1][1], m_points[x+1][y+1][2] ); glTexCoord2f( float_xb, float_y ); // 第四个纹理坐标 (右下角) glVertex3f( m_points[x+1][y][0], m_points[x+1][y][1], m_points[x+1][y][2] ); } } glEnd(); // 四边形绘制结束 } void Lesson10::initialize() { loadGLTexture(); // 加载纹理 glEnable(GL_TEXTURE_2D); // 启用纹理映射 glShadeModel(GL_SMOOTH); // 启用平滑着色 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景 glClearDepth(1.0f); // 设置深度缓存 glEnable(GL_DEPTH_TEST); // 启用深度测试 glDepthFunc(GL_LEQUAL); // 深度测试类型 // 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPolygonMode( GL_BACK, GL_FILL ); // 后表面完全填充 glPolygonMode( GL_FRONT, GL_LINE ); // 前表面使用线条绘制 // 上面的代码指定使用完全填充模式来填充多边形区域的背面(后面)。 // 相反,多边形的正面(表面)则使用轮廓线填充了。这些方式完全取决于您的个人喜好。并且与多边形的方位或者顶点的方向有关。 for(int x=0; x<45; x++) { for(int y=0; ysetFormat(requestedFormat()); m_context->create(); needsInitialize = true; } m_context->makeCurrent(this); if (needsInitialize) { initializeOpenGLFunctions(); initialize(); } render(); m_context->swapBuffers(this); } void Lesson10::loadGLTexture() { //现在载入图像,并将其转换为纹理。 QImage image(":/image/Tim.bmp"); image = image.convertToFormat(QImage::Format_RGB888); image = image.mirrored(); glGenTextures(1, &m_texture[0]);//创建纹理 //使用来自位图数据生成的典型纹理 glBindTexture(GL_TEXTURE_2D, m_texture[0]); glTexImage2D(GL_TEXTURE_2D, 0, 3,image.width(), image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE,image.bits()); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波 } void Lesson10::exposeEvent(QExposeEvent *event) { Q_UNUSED(event); if (isExposed()) { renderNow(); } } void Lesson10::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); if (isExposed()) { renderNow(); } } void Lesson10::timerEvent(QTimerEvent *event) { for(int y = 0; y < 45; y++ ) // Y平面循环 { GLfloat hold = m_points[0][y][2]; // 存储当前左侧波浪值 for(int x = 0; x < 44; x++) // 沿X平面循环 { // 当前波浪值等于其右侧的波浪值 m_points[x][y][2] = m_points[x+1][y][2]; } m_points[44][y][2]=hold; // 刚才的值成为最左侧的波浪值 } //上面所作的事情是先存储每一行的第一个值,然后将波浪左移一下,使图象产生波浪。 //存储的数值挪到末端以产生一个永无尽头的波浪纹理效果。 //上面的代码由NeHe(2000年2月)修改过,以消除波浪间出现的细小锯齿。 //现在增加 xrot , yrot 和 zrot 的值。 m_x_rotate+=0.3f; // X 轴旋转 m_y_rotate+=0.2f; // Y 轴旋转 m_z_rotate+=0.4f; // Z 轴旋转 renderNow(); QWindow::timerEvent(event); } void Lesson10::keyPressEvent(QKeyEvent *event) { int key=event->key(); switch(key) { } }
main.cpp
#include#includeint main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QSurfaceFormat format; format.setSamples(16); Lesson10 window; window.setFormat(format); window.resize(640, 480); window.show(); return app.exec(); }
运行效果