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

一、场景

在常用的项目场景中,我们经常会遇到将两个图片合在一起变成一张图片,这时候就会有前后之分,特别是两个物体合在一起的时候,从前面看就更有前后(深度)之分了,也可以理解成遮挡。这样的一种技术实现OpenGL里叫融合。

二、融合

融合:将两个或两个以上的物体或图像进行叠合,最后生成新物体或一张图像的过程;

两个物体的融合,是通过目标物体和原物体颜色的组合产生新颜色的过程;

最主要的方法是Alpha通道技术,Alpha通道是指在24位真彩色图像上加上另外8位信息,用它来描述256级不同的透明度值;
如果一个像素的Alpha通道数值为0%,那么它是完全透明的;如果Alpha通道数值为100%,那么它是完全不透明的;

要实现融合的效果,就要用到OpenGL中的glBlendFunc().

三、实现函数

绘制OpenGL的时候,有时候新的颜色和旧的颜色按照一定的方式进行混合,比如使物体拥有半透明的效果,或者绘制叠加光亮的效果,这些可以使用glBlendFunc()实现。

glBlendFunc()混合函数的函数原型是:

void glBlendFunc(GLenum srcfactor, GLenum destfactor);

它的功能就控制了新画上来的颜色(source values, RGBA),和已经在帧缓冲区的颜色(destination values, RGBA)怎么来混合。

src是源的简写,dest是目标的简写。

其中,参数 srcfactor指定了新来的颜色(source values)如何被运算,值为:

GL_ZERO,

GL_ONE,

GL_DST_COLOR,

GL_ONE_MINUS_DST_COLOR,

GL_SRC_ALPHA,

GL_ONE_MINUS_SRC_ALPHA,

GL_DST_ALPHA,

GL_ONE_MINUS_DST_ALPHA,

GL_SRC_ALPHA_SATURATE

参数 destfactor指定帧缓冲区的颜色(destination values)如何被运算。值为:

GL_ZERO,
GL_ONE,
GL_SRC_COLOR,
GL_ONE_MINUS_SRC_COLOR,
GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA

四、例程

下面就开始代码展示。

在OpenGL中实现融合的步骤类似于我们以前提到的OpenGL过程。接着设置公式,并在绘制透明对象时关闭写深度缓存。因为我们想在半透明的图形背后绘制对象。这不是正确的混色方法,但绝大多数时候这种做法在简单的项目中都工作的很好。

Rui Martins的补充:正确的融合过程应该是先绘制全部的场景之后再绘制透明的图形。并且要按照与深度缓存相反的次序来绘制(先画最远的物体)。

考虑对两个多边形(1和2)进行alpha融合,不同的绘制次序会得到不同的结果。(这里假定多边形1离观察者最近,那么正确的过程应该先画多边形2,再画多边形1。正如您再现实中所见到的那样,从这两个透明的多边形背后照射来的光线总是先穿过多边形2,再穿过多边形1,最后才到达观察者的眼睛。)

在深度缓存启用时,您应该将透明图形按照深度进行排序,并在全部场景绘制完毕之后再绘制这些透明物体。否则您将得到不正确的结果。我知道某些时候这样做是很令人痛苦的,但这是正确的方法。

头文件:

#include <QObject>
#include <QWidget>
#include <qgl.h>
#include <QTimer>
#include <QKeyEvent>
/*
*融合.
*/class NeHe_8_Widget : public QGLWidget
{Q_OBJECT
public:NeHe_8_Widget(QWidget *parent = 0);~NeHe_8_Widget();public slots:void slotTimer();protected:void initializeGL(); void paintGL();      void resizeGL( int width, int height ); void loadGLTextures(); //在这个函数中我们会载入指定的图片并生成相应当纹理。void keyPressEvent( QKeyEvent *e );GLfloat xRot, yRot, zRot ; //xRot、yRot、zRot来处理立方体在三个方向上的旋转。//GLuint texture[6];GLfloat zoom;   //zoom是场景深入屏幕的距离。GLfloat xSpeed, ySpeed; //xSpeed和ySpeed是立方体在X轴和Y轴上旋转的速度。GLuint texture[3]; //texture[3]用来存储三个纹理。GLuint filter; //filter表明的是使用哪个纹理。bool light;   //light是说明现在是否使用光源。bool blend;   //是否使用融合.
private:QTimer* m_timer;GLfloat lightAmbient[4] = { 0.5, 0.5, 0.5, 1.0 };GLfloat lightDiffuse[4] = { 1.0, 1.0, 1.0, 1.0 };GLfloat lightPosition[4] = { 0.0, 0.0, 2.0, 1.0 };
};

cpp文件实现:

#include "nehe_8_widget.h"
#include <GL/glu.h>
#include <GL/gl.h>#include <QImage>
#include <QDebug>NeHe_8_Widget::NeHe_8_Widget(QWidget *parent):QGLWidget(parent)
{xRot = yRot = zRot = 0.0;zoom = -5.0;xSpeed = ySpeed = 0.0;filter = 0;light = false;blend = false;setGeometry( 100, 200, 640, 480 );//setCaption( "NeHe's Texture Mapping Tutorial" );m_timer = new QTimer(this);m_timer->setInterval(50);
//    connect(m_timer,SIGNAL(timeout()),this,SLOT(slotTimer()));
//    m_timer->start();
}NeHe_8_Widget::~NeHe_8_Widget()
{
}void NeHe_8_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我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );glEnable( GL_LIGHT1 );glColor4f( 1.0, 1.0, 1.0, 0.8 );glBlendFunc( GL_SRC_ALPHA, GL_ONE );//上面第一行以全亮度绘制此物体,并对其进行50%的alpha融合(半透明)。当融合选项打开时,此物体将会产生50%的透明效果。\上面第二行设置所采用的融合类型。//Rui Martins的补充:alpha通道的值为0.0意味着物体材质是完全透明的。1.0则意味着完全不透明。
}void NeHe_8_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_8_Widget::paintGL()
{glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );//清楚屏幕和深度缓存。glLoadIdentity();//重置当前的模型观察矩阵。glTranslatef(  0.0,  0.0, zoom);glRotatef( xRot,  1.0,  0.0,  0.0 );glRotatef( yRot,  0.0,  1.0,  0.0 );//glRotatef( zRot,  0.0,  0.0,  1.0 );//根据xRot、yRot、zRot的实际值来旋转正方体。glBindTexture( GL_TEXTURE_2D, texture[filter] );glBegin( GL_QUADS );glNormal3f( 0.0, 0.0, 1.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0,  1.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0,  1.0 );glNormal3f( 0.0, 0.0, -1.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0, -1.0 );glNormal3f( 0.0, 1.0, 0.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0,  1.0,  1.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0,  1.0,  1.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );glNormal3f( 0.0, -1.0, 0.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0, -1.0, -1.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0, -1.0, -1.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );glNormal3f( 1.0, 0.0, 0.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f(  1.0, -1.0, -1.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f(  1.0,  1.0, -1.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f(  1.0,  1.0,  1.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f(  1.0, -1.0,  1.0 );glNormal3f( -1.0, 0.0, 0.0 );glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0,  1.0 );glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0,  1.0,  1.0 );glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0,  1.0, -1.0 );glEnd();xRot += xSpeed;yRot += ySpeed;
}//loadGLTextures()函数就是用来载入纹理的。
void NeHe_8_Widget::loadGLTextures()
{QImage tex,buf;if (!buf.load( ":/data/Glass.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( 3, &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() );glBindTexture( GL_TEXTURE_2D, texture[1] );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() );glBindTexture( GL_TEXTURE_2D, texture[2] );glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, tex.width(), tex.height(), GL_RGBA,\GL_UNSIGNED_BYTE, tex.bits() );}void NeHe_8_Widget::slotTimer()
{xRot += 0.3;yRot += 0.2;zRot += 0.4;update();
}void NeHe_8_Widget::keyPressEvent( QKeyEvent *e )
{switch ( e->key() ){case Qt::Key_L: //按下了L键,就可以切换是否打开光源。light = !light;if ( !light ){glDisable( GL_LIGHTING );}else{glEnable( GL_LIGHTING );}updateGL();break;case Qt::Key_B: //按下了B键,就可以切换是否启用融合方式。blend = !blend;if ( blend ){glEnable( GL_BLEND );glDisable( GL_DEPTH_TEST );}else{glDisable( GL_BLEND );glEnable( GL_DEPTH_TEST );}updateGL();break;case Qt::Key_F: //按下了F键,就可以转换一下所使用的纹理(就是变换了纹理滤波方式的纹理)。filter += 1;;if ( filter > 2 ){filter = 0;}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。xSpeed -= 0.01;updateGL();break;case Qt::Key_S: //按下了Dowm方向键,增加xSpeed。xSpeed += 0.01;updateGL();break;case Qt::Key_A: //按下了Right方向键,增加ySpeed。ySpeed += 0.01;updateGL();break;case Qt::Key_D: //按下了Left方向键,减少ySpeed。ySpeed -= 0.01;updateGL();break;case Qt::Key_Escape:close();}}

但是怎样才能在使用纹理贴图的时候指定融合时的颜色呢?很简单,在调整贴图模式时,文理贴图的每个象素点的颜色都是由alpha通道参数与当前地象素颜色相乘所得到的。比如,绘制的颜色是(0.5, 0.6, 0.4),我们会把颜色相乘得到(0.5, 0.6, 0.4, 0.2)(alpha参数在没有指定时,缺省为零)。

原文注 (1999年11月13日)

融合代码进行了修改,以使显示的物体看起来更逼真。同时对源象素和目的象素使用alpha参数来融合,会导致物体的人造痕迹看起来很明显。会使得物体的背面沿着侧面的地方显得更暗。基本上物体会看起来很怪异。我所用的融合方法也许不是最好的,但的确能够工作。启用光源之后,物体看起来很逼真。感谢Tom提供的原始代码,他采用的混色方法是正确的,但物体看起来并不象所期望的那样吸引人:)代码所作的再次修改是因为在某些显卡上glDepthMask()函数存在寻址问题。这条命令在某些卡上启用或关闭深度缓冲测试时似乎不是很有效,所以我已经将启用或关闭深度缓冲测试的代码转成老式的glEnable和glDisable。

纹理贴图的alpha融合

用于纹理贴图的alpha参数可以象颜色一样从问题贴图中读取。方法如下,您需要在载入所需的材质同时取得其的alpha参数。然后在调用glTexImage2D()时使用GL_RGBA的颜色格式。

五、运行效果

代码编写完毕后,运行看一下效果。

1、运行效果:

2、看一下旋转效果

3、光源打开关闭

4、纹理的切换

5、融合启用和关闭

六、图片链接

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

链接:https://pan.baidu.com/s/1BxNurzF9j0E9lH1RNZ-YPg 
提取码:6lct

上一篇:OpenGL(十五)——Qt OpenGL三种不同的纹理滤波方式、光照、物体的移动

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

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

OpenGL(十六)——Qt OpenGL融合(将两张图片叠合成一张图片)相关推荐

  1. Qt OpenGL(三十六)——Qt OpenGL 核心模式-绘制雷达坐标系

    提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(三十六)--Qt OpenGL 核心模式-绘制雷达坐标系 一.场景 ...

  2. Qt OpenGL(二十)——Qt OpenGL 核心模式版本

    Qt OpenGL(二十)--Qt OpenGL 核心模式版本 一.写在前面 在之前的OpenGL教程(1~19)中,采用的方式都是固定渲染管线,也就是OpenGL3.2版本之前的写法,但是OpenG ...

  3. 将两张(多张图片)合成一张图片

    你在开发过程中,有时候会遇到需要将两张图,或者几张图合成一张图,例如在图片分享过程中 下面代码可以为你解忧 -(UIImage *)addImage:(UIImage *)image1 toImage ...

  4. 【OpenGL】十六、OpenGL 绘制四边形 ( 绘制 GL_QUADS 四边形 )

    文章目录 一.绘制 GL_QUADS 四边形 二.绘制多个四边形 三.相关资源 一.绘制 GL_QUADS 四边形 四边形绘制时 , 在 glBegin 方法中传入 GL_QUADS 参数 , 其绘制 ...

  5. 三张图片无缝合成一张图片_如何实现两张图片的无缝结合

    预览成品: 逐步说明: 1.看见预览图了吗,就是把右图的男孩脸换成女孩的脸.现在我们来具体制作这个效果. 2.单击菜单"File > Open"(文件-打开)打开两张张图象, ...

  6. OpenGL(十一)——Qt OpenGL给多边形上色

    OpenGL(十一)--Qt OpenGL给多边形上色 一.前言 上篇文章介绍了绘制多边形的代码. 本篇介绍给多边形上色. 上篇的运行效果: 二.代码 上一篇中三角形和四边形的绘制方法.这一篇给三角形 ...

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

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

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

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

  9. NeHe OpenGL教程 第三十六课:从渲染到纹理

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

最新文章

  1. tightvnc viewer 传文件_TightVNC中文版下载_TightVNC viewer(远程控制软件)简体中文版下载【32位|64位】-华军软件园...
  2. Kettle使用_8 存储过程结合获取系统信息
  3. QQ聊天文字背影图片拉伸方法
  4. DHCP 服务原理:Snooping和Relay
  5. Spring MVC错误处理流程
  6. cnn训练出现的问题
  7. c 与mysql连接_c与mysql连接和一个简单查询的例子
  8. Docker安装nginx以及负载均衡
  9. ps黑作坊终极天空大师 一键换天空终极版破解版扩展面板
  10. Tensorflow从指定链接下载文件
  11. MATLAB 神经网络函数
  12. ~《概率论》~贝叶斯公式
  13. QQ截图时窗口自动识别的原理
  14. excel函数技巧:MAX在数字查找中的应用妙招
  15. ESP32 CAM与服务器(python)UDP视频传输
  16. mybatis 实现动态数据源连接
  17. 程序员不学会做饭,无异于慢性自杀!
  18. 学计算机和电子信息工程那个更好找工作,电子信息工程专业毕业月薪一般是多少 好不好找工作...
  19. linux系统的界面是什么样的,Linux系统是什么样子的
  20. edup无线网卡驱动安装linux,802.11n无线网卡驱动-edup 802.11n驱动下载最新版-802.11n驱动西西软件下载...

热门文章

  1. 面积计算9860SD计算器程序(好用就用,不用就删-----歪XX)
  2. 国外常用浏览器User-Agent汇总
  3. 计算机文档字体替换,word2007进行字体替换的两种方法
  4. ArcGis for js 查询定位、缩放致
  5. 冰达ROS机器人使用-实现slam建模、自主导航、避障
  6. mybatis(三) XML映射器之select、update、delete、insert标签
  7. 谷歌上做SEO价钱大概多少,Google优化怎么收费?
  8. BUCTOJ Contest1001 - 邀请赛20180814 问题 F: Poker
  9. ZZULIOJ:1125: 上三角矩阵的判断
  10. 双向晶闸管控制AC220V电机