OpenGL(十六)——Qt OpenGL融合(将两张图片叠合成一张图片)
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融合(将两张图片叠合成一张图片)相关推荐
- Qt OpenGL(三十六)——Qt OpenGL 核心模式-绘制雷达坐标系
提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(三十六)--Qt OpenGL 核心模式-绘制雷达坐标系 一.场景 ...
- Qt OpenGL(二十)——Qt OpenGL 核心模式版本
Qt OpenGL(二十)--Qt OpenGL 核心模式版本 一.写在前面 在之前的OpenGL教程(1~19)中,采用的方式都是固定渲染管线,也就是OpenGL3.2版本之前的写法,但是OpenG ...
- 将两张(多张图片)合成一张图片
你在开发过程中,有时候会遇到需要将两张图,或者几张图合成一张图,例如在图片分享过程中 下面代码可以为你解忧 -(UIImage *)addImage:(UIImage *)image1 toImage ...
- 【OpenGL】十六、OpenGL 绘制四边形 ( 绘制 GL_QUADS 四边形 )
文章目录 一.绘制 GL_QUADS 四边形 二.绘制多个四边形 三.相关资源 一.绘制 GL_QUADS 四边形 四边形绘制时 , 在 glBegin 方法中传入 GL_QUADS 参数 , 其绘制 ...
- 三张图片无缝合成一张图片_如何实现两张图片的无缝结合
预览成品: 逐步说明: 1.看见预览图了吗,就是把右图的男孩脸换成女孩的脸.现在我们来具体制作这个效果. 2.单击菜单"File > Open"(文件-打开)打开两张张图象, ...
- OpenGL(十一)——Qt OpenGL给多边形上色
OpenGL(十一)--Qt OpenGL给多边形上色 一.前言 上篇文章介绍了绘制多边形的代码. 本篇介绍给多边形上色. 上篇的运行效果: 二.代码 上一篇中三角形和四边形的绘制方法.这一篇给三角形 ...
- OpenGL(十八)——Qt OpenGL绘制一个3D世界
OpenGL(十八)--Qt OpenGL绘制一个3D世界 一.说明 本篇介绍构建一个3D的世界. 二.简介 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游. ...
- OpenGL(十四)——Qt OpenGL纹理
OpenGL(十四)--Qt OpenGL纹理 一.纹理 终于写到纹理的部分了: 纹理(Texture)的本质是一个2D图片(1D和3D),或者叫图形数据.只是在OpenGL中专业术语中称其为纹理. ...
- NeHe OpenGL教程 第三十六课:从渲染到纹理
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
最新文章
- tightvnc viewer 传文件_TightVNC中文版下载_TightVNC viewer(远程控制软件)简体中文版下载【32位|64位】-华军软件园...
- Kettle使用_8 存储过程结合获取系统信息
- QQ聊天文字背影图片拉伸方法
- DHCP 服务原理:Snooping和Relay
- Spring MVC错误处理流程
- cnn训练出现的问题
- c 与mysql连接_c与mysql连接和一个简单查询的例子
- Docker安装nginx以及负载均衡
- ps黑作坊终极天空大师 一键换天空终极版破解版扩展面板
- Tensorflow从指定链接下载文件
- MATLAB 神经网络函数
- ~《概率论》~贝叶斯公式
- QQ截图时窗口自动识别的原理
- excel函数技巧:MAX在数字查找中的应用妙招
- ESP32 CAM与服务器(python)UDP视频传输
- mybatis 实现动态数据源连接
- 程序员不学会做饭,无异于慢性自杀!
- 学计算机和电子信息工程那个更好找工作,电子信息工程专业毕业月薪一般是多少 好不好找工作...
- linux系统的界面是什么样的,Linux系统是什么样子的
- edup无线网卡驱动安装linux,802.11n无线网卡驱动-edup 802.11n驱动下载最新版-802.11n驱动西西软件下载...
热门文章
- 面积计算9860SD计算器程序(好用就用,不用就删-----歪XX)
- 国外常用浏览器User-Agent汇总
- 计算机文档字体替换,word2007进行字体替换的两种方法
- ArcGis for js 查询定位、缩放致
- 冰达ROS机器人使用-实现slam建模、自主导航、避障
- mybatis(三) XML映射器之select、update、delete、insert标签
- 谷歌上做SEO价钱大概多少,Google优化怎么收费?
- BUCTOJ Contest1001 - 邀请赛20180814 问题 F: Poker
- ZZULIOJ:1125: 上三角矩阵的判断
- 双向晶闸管控制AC220V电机