这次教程中我们教介绍OpenGL的蒙板技术。到目前为止我们已经学会如何使用alpha混合把一个透明物体渲染到屏幕上了但有时使用它看起来并不是那么的复合我们的心意。使用蒙板技术将会使图像按照我们设定的蒙板位置精确地绘制。
直到现在我们在把图像加载到屏幕上时都没有檫除背景色因为这样简单高效但是效果并不总是很好。大部分情况下把纹理混合到屏幕纹理不是太少就是太多。当我们使用精灵图时我们不希望背景从精灵的缝隙中透出光来但在显示文字时我们又希望文字的间隙可以显示背景色。
基于上述原因我们需要使用“掩模”。使用“掩膜”需要两个步骤首先我们在场景上放置黑白相间的纹理白色代表透明部分黑色代表不透明部分。接着我们使用一种特殊的混合方式只有在黑色部分上的纹理才会显示在场景中。
程序运行时效果如下
下面进入教程
我们这次将在第06课代码的基础上修改代码总体上并不会太难希望大家能理解蒙板技术这技术真的很好用。首先打开myglwidget.h文件将类声明更改如下
1 #ifndef MYGLWIDGET_H 2 #define MYGLWIDGET_H 3 4 #include 5 #include 6 7 class MyGLWidget : public QGLWidget 8 { 9 Q_OBJECT10 public:11 explicit MyGLWidget(QWidget *parent 0);12 ~MyGLWidget();13 14 protected:15 //对3个纯虚函数的重定义16 void initializeGL();17 void resizeGL(int w, int h);18 void paintGL();19 20 void keyPressEvent(QKeyEvent *event); //处理键盘按下事件21 22 private:23 bool fullscreen; //是否全屏显示24 bool m_Masking; //是否使用"掩模"25 bool m_Scene; //控制绘制哪一层26 27 GLfloat m_Rot; //控制纹理滚动28 QString m_FileName[5]; //图片的路径及文件名29 GLuint m_Texture[5]; //储存五个纹理30 };31 32 #endif // MYGLWIDGET_H
本文福利莬费领取Qt开发学习资料包、技术视频内容包括C语言基础Qt编程入门QT信号与槽机制QT界面开发-图像绘制QT网络QT数据库编程QT项目实战QT嵌入式开发Quick模块面试题等等↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
我们增加了两个布尔变量m_Masking和m_Scene来控制是否开启“ 掩模”以及绘制哪一个场景。然后我们增加一个控制图形滚动旋转的变量m_Rot当然要去掉之前控制旋转的变量。最后把m_FileName和m_Texture变成长度为5的数组因为我们需要载入5个纹理。
接下来我们打开myglwidget.cpp在构造函数中对新增变量进行初始化比较简单大家参照注释理解不多作解释具体代码如下
1 MyGLWidget::MyGLWidget(QWidget *parent) : 2 QGLWidget(parent) 3 { 4 fullscreen false; 5 m_Masking true; 6 m_Scene false; 7 8 m_FileName[0] "D:/QtOpenGL/QtImage/Logo.bmp"; //纹理0 9 m_FileName[1] "D:/QtOpenGL/QtImage/Mask1.bmp"; //掩模纹理1,作为"掩模"使用10 m_FileName[2] "D:/QtOpenGL/QtImage/Image1.bmp"; //纹理111 m_FileName[3] "D:/QtOpenGL/QtImage/Mask2.bmp"; //掩模纹理2,作为"掩模"使用12 m_FileName[4] "D:/QtOpenGL/QtImage/Image2.bmp"; //纹理213 14 QTimer *timer new QTimer(this); //创建一个定时器15 //将定时器的计时信号与updateGL()绑定16 connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));17 timer->start(10); //以10ms为一个计时周期18 }
然后我们略微修改下initializeGL()函数就是载入5个位图并转换成纹理不多解释了具体代码如下
1 void MyGLWidget::initializeGL() //此处开始对OpenGL进行所以设置 2 { 3 //载入位图并转换成纹理 4 for (int i0; i<5; i){ 5 m_Texture[i] bindTexture(QPixmap(m_FileName[i])); 6 } 7 glEnable(GL_TEXTURE_2D); //启用纹理映射 8 9 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //黑色背景10 glShadeModel(GL_SMOOTH); //启用阴影平滑11 glClearDepth(1.0); //设置深度缓存 12 }
继续我们要进入最有趣的paintGL()函数当然这也是重点具体代码如下
1 void MyGLWidget::paintGL() //从这里开始进行所以的绘制 2 { 3 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存 4 glLoadIdentity(); //重置模型观察矩阵 5 glTranslatef(0.0f, 0.0f, -2.0f); //移入屏幕2.0单位 6 7 glBindTexture(GL_TEXTURE_2D, m_Texture[0]); //选择Logo纹理 8 glBegin(GL_QUADS); //绘制纹理四边形 9 glTexCoord2f(0.0f, -m_Rot0.0f);10 glVertex3f(-1.1f, -1.1f, 0.0f);11 glTexCoord2f(3.0f, -m_Rot0.0f);12 glVertex3f(1.1f, -1.1f, 0.0f);13 glTexCoord2f(3.0f, -m_Rot3.0f);14 glVertex3f(1.1f, 1.1f, 0.0f);15 glTexCoord2f(0.0f, -m_Rot3.0f);16 glVertex3f(-1.1f, 1.1f, 0.0f);17 glEnd();18 19 glEnable(GL_BLEND); //启用混合20 glDisable(GL_DEPTH_TEST); //禁用深度测试21 22 if (m_Masking) //是否启用"掩模"23 {24 glBlendFunc(GL_DST_COLOR, GL_ZERO); //使用黑白"掩模"25 }26 27 if (m_Scene)28 {29 glTranslatef(0.0f, 0.0f, -1.0f); //移入屏幕1.0单位30 glRotatef(m_Rot*360, 0.0f, 0.0f, 1.0f); //绕z轴旋转31 32 if (m_Masking) //"掩模"是否打开33 {34 glBindTexture(GL_TEXTURE_2D, m_Texture[3]); //选择第二个"掩模"纹理35 glBegin(GL_QUADS); //开始绘制四边形36 glTexCoord2f(0.0f, 0.0f);37 glVertex3f(-1.1f, -1.1f, 0.0f);38 glTexCoord2f(1.0f, 0.0f);39 glVertex3f(1.1f, -1.1f, 0.0f);40 glTexCoord2f(1.0f, 1.0f);41 glVertex3f(1.1f, 1.1f, 0.0f);42 glTexCoord2f(0.0f, 1.0f);43 glVertex3f(-1.1f, 1.1f, 0.0f);44 glEnd();45 }46 47 glBlendFunc(GL_ONE, GL_ONE); //把纹理2复制到屏幕上48 glBindTexture(GL_TEXTURE_2D, m_Texture[4]); //选择第二个纹理49 glBegin(GL_QUADS); //绘制四边形50 glTexCoord2f(0.0f, 0.0f);51 glVertex3f(-1.1f, -1.1f, 0.0f);52 glTexCoord2f(1.0f, 0.0f);53 glVertex3f(1.1f, -1.1f, 0.0f);54 glTexCoord2f(1.0f, 1.0f);55 glVertex3f(1.1f, 1.1f, 0.0f);56 glTexCoord2f(0.0f, 1.0f);57 glVertex3f(-1.1f, 1.1f, 0.0f);58 glEnd();59 }60 else61 {62 if (m_Masking) //"掩模"是否打开63 {64 glBindTexture(GL_TEXTURE_2D, m_Texture[1]); //选择第一个"掩模"纹理65 glBegin(GL_QUADS); //绘制四边形66 glTexCoord2f(m_Rot0.0f, 0.0f);67 glVertex3f(-1.1f, -1.1f, 0.0f);68 glTexCoord2f(m_Rot4.0f, 0.0f);69 glVertex3f(1.1f, -1.1f, 0.0f);70 glTexCoord2f(m_Rot4.0f, 4.0f);71 glVertex3f(1.1f, 1.1f, 0.0f);72 glTexCoord2f(m_Rot0.0f, 4.0f);73 glVertex3f(-1.1f, 1.1f, 0.0f);74 glEnd();75 }76 77 glBlendFunc(GL_ONE, GL_ONE); //把纹理1复制到屏幕78 glBindTexture(GL_TEXTURE_2D, m_Texture[2]); //选择第一个纹理79 glBegin(GL_QUADS); //绘制四边形80 glTexCoord2f(m_Rot0.0f, 0.0f);81 glVertex3f(-1.1f, -1.1f, 0.0f);82 glTexCoord2f(m_Rot4.0f, 0.0f);83 glVertex3f(1.1f, -1.1f, 0.0f);84 glTexCoord2f(m_Rot4.0f, 4.0f);85 glVertex3f(1.1f, 1.1f, 0.0f);86 glTexCoord2f(m_Rot0.0f, 4.0f);87 glVertex3f(-1.1f, 1.1f, 0.0f);88 glEnd();89 }90 91 glEnable(GL_DEPTH_TEST); //启用深度测试92 glDisable(GL_BLEND); //禁用混合93 94 m_Rot 0.002f; //增加调整纹理滚动旋转变量95 if (m_Rot > 1.0f)96 {97 m_Rot - 1.0f;98 }99 }
函数一开始清除背景色重置矩阵把物体移入屏幕2.0单位。接着我们选择logo纹理绘制纹理四边形注意到我们调用glTexCoord选择纹理坐标时有的数是大于1.0的这时候OpenGL默认截取小数部分进行处理这样就可以得到无缝的循环纹理具体效果大家看上面的图或自己运行程序时再看。然后我们启用混合并禁用深度测试。
接着我们需要根据m_Masking的值设置是否使用“掩模”如果是我们需要设置相应的混合因子。一个“掩模”只是一幅绘制到屏幕的纹理图片但只有黑色和白色白色的部分代表透明黑色的部分代表不透明。我们设置的混合因子GL_DST_COLOR、GL_ZERO使得任何纹理OpenGL并不知道这是不是“掩模”黑色的部分会变为黑色白色的部分会保持原来的颜色就是变成透明透过了原来的颜色。
然后我们检查是绘制哪一个场景图层true绘制第二层false绘制第一层。true时先开始绘制第二层为了不使得它看起来太大我们把它移入屏幕1.0单位并把它按m_Rot的值绕z轴旋转。接着我们检查m_Marking的值如果为true我们就把“掩模”绘制到屏幕上当我们完成这个操作时将会看到一个镂空的纹理出现在屏幕上。然后我们变换混合因子GL_ONE、GL_ONE这次我们告诉OpenGL把任何黑色部分对应的像素复制到屏幕这样看起来纹理就像被镂空一样贴在屏幕上。要注意的是我在变换了混合因子后才选择的纹理。如果我们没有使用 “掩模”我们的图像将与屏幕颜色融合。
下面我绘制第一层与第二层的绘制基本相同不多解释了。最后我们启用深度测试禁用混合然后增加m_Rot变量如果大于1.0把它的值减去1.0。
最后我们修改一下键盘控制函数就是加上了空格和M键作为切换键很简单不多解释了具体代码如下
1 void MyGLWidget::keyPressEvent(QKeyEvent *event) 2 { 3 switch (event->key()) 4 { 5 case Qt::Key_F1: //F1为全屏和普通屏的切换键 6 fullscreen !fullscreen; 7 if (fullscreen) 8 { 9 showFullScreen();10 }11 else12 {13 showNormal();14 }15 updateGL();16 break;17 case Qt::Key_Escape: //ESC为退出键18 close();19 break;20 case Qt::Key_Space: //空格为场景(图层)的切换键21 m_Scene !m_Scene;22 break;23 case Qt::Key_M: //M为是否"掩膜"的切换键24 m_Masking !m_Masking;25 break;26 }27 }
现在就可以运行程序查看效果了
一点内容的补充上面我们提到当调用glTexCoord选择纹理坐标时如果大于1.0OpenGL默认截取小数部分进行处理。其实这只是OpenGL默认的处理模式GL_REPEAT。对于纹理坐标大于1.0OpenGL有以下几种处理模式
GL_CLAMP - 截取
GL_REPEAT - 重复OpenGL默认的模式
GL_MIRRORED_REPEAT - 镜像重复
GL_CLAMP_TO_EDGE - 忽略边框截取
GL_CLAMP_TO_BORDER - 带边框的截取
我们可以利用glTexParameter函数来进行模式的转换如x方向的转换为glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)变换模式只需更改第三个参数。而第二参数代表方向GL_TEXTURE_WRAP_S代表x方向GL_TEXTURE_WRAP_T代表y方向GL_TEXTURE_WRAP_R代表z方向。
本文福利莬费领取Qt开发学习资料包、技术视频内容包括C语言基础Qt编程入门QT信号与槽机制QT界面开发-图像绘制QT网络QT数据库编程QT项目实战QT嵌入式开发Quick模块面试题等等↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓