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_Hlessson10.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();
}运行效果





