`
gongzhq
  • 浏览: 22722 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

第八课:融合

阅读更多



 OpenGL中的绝大多数特效都与某些类型的(色彩)融合有关。融合的定义为,将某个象素的颜色和已绘制在屏幕上与其对应的象素颜色相互结合。至于如何结合这两个颜色则依赖于颜色的alpha通道的分量值,以及/或者所使用的融合函数。alpha通常是位于颜色值末尾的第4个颜色组成分量。前面这些课我们都是用GL_RGB来指定颜色的三个分量。相应的GL_RGBA可以指定alpha分量的值。更进一步,我们可以使用glColor4f()来代替 glColor3f()。

绝大多数人都认为alpha分量代表材料的透明度。这就是说,alpha值为0.0时所代表的材料是完全透明的。alpha值为1.0时所代表的材料则是完全不透明的。

融合的公式

若您对数学不感冒,而只想看看如何实现透明,请跳过这一节。若您想深入理解(色彩)融合的工作原理,这一节应该适合您吧。(CKER注:其实并不难^- ^。原文中的公式如下,CKER再唠叨一下吧。其实融合的基本原理是就将要分色的图像各象素的颜色以及背景颜色均按照RGB规则各自分离之后,根据-图像的RGB颜色分量*alpha值+背景的RGB颜色分量*(1-alpha值)——这样一个简单公式来融合之后,最后将融合得到的RGB分量重新合并。)

公式如下:

(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)

OpenGL按照上面的公式计算这两个象素的融合结果。小写的s和r分别代表源象素和目标象素。大写的S和D则是相应的融合因子。这些决定了您如何对这些象素融合。绝大多数情况下,各颜色通道的alpha融合值大小相同,这样对源象素就有 (As, As, As, As),目标象素则有1, 1, 1, 1) - (As, As, As, As)。上面的公式就成了下面的模样:

(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bs (1 - As), As As + Ad (1 - As))

这个公式会生成透明/半透明的效果。

OpenGL中的融合

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

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

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

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

我们要在上一课的代码上进行改动就可以了。

我们将增加一些变量,稍后我们对这些变量进行解释。
NeHeWidget类

(由nehewidget.h展开。)

protected:

  bool fullscreen;

  GLfloat xRot, yRot, zRot;
  GLfloat zoom;
  GLfloat xSpeed, ySpeed;
  GLuint texture[3];
  GLuint filter;
 
  bool light;
  bool blend;
 
};

比上一课,只增加了blend这个变量,说明现在是否使用融合。

(由nehewidget.cpp展开。)

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 };

这里定义了三个数组,它们描述的是和光源有关的信息。这里使用的光源和上一课一样。

NeHeWidget::NeHeWidget( QWidget* parent, bool fs )
    : QGLWidget( parent )
{
  xRot = yRot = zRot = 0.0;
  zoom = -5.0;
  xSpeed = ySpeed = 0.0;

  filter = 0;

  light = false;
  blend = false;

  fullscreen = fs;
  setGeometry( 0, 0, 640, 480 );
  setWindowTitle(tr( "Tom Stanis & NeHe's Blending Tutorial") );

  if ( fullscreen )
showFullScreen();
  startTimer(5);
}

我们需要在构造函数中给各个变量赋初值。xRot、yRot、zRot是0.0。zoom是-5.0。xSpeed和ySpeed都是0。filter是0。light是false。blend是false。

void NeHeWidget::loadGLTextures()
{
  QImage tex, buf;
  if ( !buf.load( "./ui/Glass.bmp" ) )
  {
    qWarning( "Could not read image file, using single-color instead." );
    QImage dummy( 128, 128, QImage::Format_RGB32 );
    dummy.fill( QColor(Qt::green).rgb() );;
    buf = dummy;
  }
  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() );
}

loadGLTextures()函数就是用来载入纹理的。三个纹理的滤波方式都和上一课一样。

void NeHeWidget::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 );

  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;
}

这里也和上一课一样,主要还是画一个立方体。

void NeHeWidget::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 );
 
  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.5 );
  glBlendFunc( GL_SRC_ALPHA, GL_ONE );

上面第一行以全亮度绘制此物体,并对其进行50%的alpha融合(半透明)。当融合选项打开时,此物体将会产生50%的透明效果。上面第二行设置所采用的融合类型。

Rui Martins的补充:alpha通道的值为0.0意味着物体材质是完全透明的。1.0则意味着完全不透明。

}

void NeHeWidget::keyPressEvent( QKeyEvent *e )
{
  switch ( e->key() )
  {
  case Qt::Key_L:
    light = !light;
    if ( !light )
    {
      glDisable( GL_LIGHTING );
    }
    else
    {
      glEnable( GL_LIGHTING );
    }
    updateGL();
    break;
  case Qt::Key_B:
    blend = !blend;
    if ( blend )
    {
      glEnable( GL_BLEND );
      glDisable( GL_DEPTH_TEST );
    }
    else
    {
      glDisable( GL_BLEND );
      glEnable( GL_DEPTH_TEST );
    }
    updateGL();
    break;

按下了B键,就可以切换是否启用融合方式。

  case Qt::Key_F:
    filter += 1;;
    if ( filter > 2 )
    {
      filter = 0;
    }
    updateGL();
    break;
  case Qt::Key_PageUp:
    zoom -= 0.2;
    updateGL();
    break;
  case Qt::Key_PageDown:
    zoom += 0.2;
    updateGL();
    break;
  case Qt::Key_Up:
    xSpeed -= 0.01;
    updateGL();
    break;
  case Qt::Key_Down:
    xSpeed += 0.01;
    updateGL();
    break;
  case Qt::Key_Right:
    ySpeed += 0.01;
    updateGL();
    break;
  case Qt::Key_Left:
    ySpeed -= 0.01;
    updateGL();
    break;
  case Qt::Key_F2:
    fullscreen = !fullscreen;
    if ( fullscreen )
    {
      showFullScreen();
    }
    else
    {
      showNormal();
      setGeometry( 0, 0, 640, 480 );
    }
    update();
    break;
  case Qt::Key_Escape:
    close();
  }
}

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

就是如此!OpenGL实现alpha融合的确很简单!

原文注 (1999年11月13日)

我(NeHe)融合代码进行了修改,以使显示的物体看起来更逼真。同时对源象素和目的象素使用alpha参数来融合,会导致物体的人造痕迹看起来很明显。

会使得物体的背面沿着侧面的地方显得更暗。基本上物体会看起来很怪异。我所用的融合方法也许不是最好的,但的确能够工作。启用光源之后,物体看起来很逼真。感谢Tom提供的原始代码,他采用的混色方法是正确的,但物体看起来并不象所期望的那样吸引人:)

代码所作的再次修改是因为在某些显卡上glDepthMask()函数存在寻址问题。这条命令在某些卡上启用或关闭深度缓冲测试时似乎不是很有效,所以我已经将启用或关闭深度缓冲测试的代码转成老式的glEnable和glDisable。

纹理贴图的alpha融合

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

  • 大小: 178.2 KB
0
0
分享到:
评论

相关推荐

    柯尔特教程 CoRT 创造性思维

    第8课:备选方案(APC)产生新的方案和选择,而不要限制在原来的方案上面。 第9课:判断。不同的方法,总结前面两课的大部分内容。 第10课:他人的观点(OPV)暂时放下自己的观点,考虑所有其他人对相关情况的观点。...

    知识图谱课程 全套视频+代码+课件

    非常全面的知识图谱课程,全套视频+代码+课件 从人工智能到开放知识图谱 ...第八讲 语义搜索 第九课:知识问答 第十课:IBM watson Lite 第十课:语义搜索+知识问答Demo 第十一课:行业知识图谱应用

    知识图谱学习资料打包

    第八讲 语义搜索.pdf 第六讲 知识融合.pdf 第十一课:行业知识图谱应用.pdf 第十课 IBM watson Lite.pdf 第十课:语义搜索+知识问答Demo.pdf 第十课:语义搜索和知识问答Demo.pdf 第四讲 知识抽取与挖掘_...

    VC学习大纲 VC学习讲义

    第八课.: 对话框用户界面程序的编写,如何向对话框控件联接数据成员及其实现机理,如何向对话框控关联控件类,如何利用对话框类的成员函数向控件发送消息和获取对话框控件的类指针,如何直接利用对话框控件类操纵...

    迈向高级的Java面试突围课.rar

    一面下半场——项目业务问题解决第4章 二面基本——扎实的基础能力第5章 二面进阶——应用程序高性能第6章 二面深入——微服务和架构认知第7章 三面上半场——容器化/云原生/安全监控第8章 三面下半场——大数据/...

    计算机应用基础课程诊改汇报.pptx

    师生互动次数少 一、课程现状 一、课程现状 存在问题 课程团队 存在问题 存在问题 一、诊改基础 课程定位 课程定位 课程资源 课程概况 课程团队 课证融合 计算机应用基础课程诊改汇报全文共45页,当前为第8页。...

    极课大数据对班级教学的SPSS分析报告.doc

    极课大数据对班级教学的SPSS分析报告 作者:曹美阳 来源:《新课程研究·上旬》2018年第11期 摘 要:教育信息化带来了教与学方式的深刻变革,大数据已经与教师的日常教学深度融合 ,如何利用新技术实现教学的个性化...

    计算机应用专业课程诊改汇报.pptx

    职业能力中级 职业能力高级 岗前综合技能培训 和顶岗实训模块 职业综合能力提升 第四阶段 第五阶段 时间 二、专业人才培养模式 计算机应用专业课程诊改汇报全文共24页,当前为第8页。 1 2 3 4 固本培根,夯实基础,...

    IoT-Note:物联网工程概论笔记

    第八章 数据融合 云计算 数据挖掘 第九章 安全防护 体系结构 攻防类型 第十章 (基本不考)对设计题有帮助 === 以下是刘佳亮部分 ## 知识点总结(考不考就不知道了) 第一章 物联网概述 物联网的主要特征是:全面...

    信息系统与数据库技术教学大纲.docx

    信息系统与数据库技术教学大纲全文共8页,当前为第1页。信息系统与数据库技术教学大纲全文共8页,当前为第1页。信息系统与数据库技术教学大纲 信息系统与数据库技术教学大纲全文共8页,当前为第1页。 信息系统与...

    计算机硬件的合理配置教学设计(1).doc

    计算机硬件的合理配置教学设计 九年级《信息技术》 第二单元第八节教学设计 课题:合理进行计算机硬件的配置 合理进行计算机硬件的配置 --主要硬件配置的合理性探讨 一、课例背景信息 1.年级:初三年级 2.所用教材...

    计算机硬件的合理配置教学设计.doc

    九年级《信息技术》 第二单元第八节教学设计 课题:合理进行计算机硬件的配置 合理进行计算机硬件的配置 --主要硬件配置的合理性探讨 一、课例背景信息 1.年级:初三年级 2.所用教材版本:我市信息技术编委会(我省...

    《计算机安全》教学设计.doc

    《计算机安全》教学设计 一、教材、课题分析 1、教材地位和作用 本课题计算机安全是陕西科学技术出版社《信息技术基础》教材第一章(有效获取信 息)第六节计算机安全的内容。信息安全知识在整本教材中有着举足轻重...

    中职计算机应用基础说课稿-图文混排.doc

    八、教学设计的几点思考 1(本节课突出以任务驱动为主线,以学生的自主学习为中心,让学生善于发现问题并 解决问题,充分调动学生的学习积极性,学生勤于动手,积极思考,课堂气氛活跃,教 学效果好。 2(如何让基础...

    走进信息世界主题活动方案设计.doc

    走进信息世界 主题活动方案设计 活动主题:《走进信息世界》 【活动背景】 综合实践活动课主要体现为学科知识的综合运用、听说读写能力的整体发展、课程间的 沟通融合、书本学习和实践活动的紧密结合,而实践是第一...

    KesionEDU在线网校系统-.net

    科汛在线网校系统V8(简称V8)是KESION科汛旗下的一款新教育OMO产品,从2014年发布第一个版本至今已经历了8大版本,上百个更新迭代小版本。先后协助超过10万家的教育培训机构、企业、学校等做线上教育转型。而V8产品...

    C# Winform数据库应用设计(附开发案例

    第8章使用 Data Reader完成查询功能 177 核心技能部分… 178 8.1 DataReader对象 811 Data reader对象简介 178 812 Data Reader对象常用的属性和方法 813 Data reader对象的使用 179 8.2 控件 81 821 Listview控件...

    二十三种设计模式【PDF版】

    者将面向对象的思想巧妙的融合在 Java 的具体技术上,潜移默化的让你感觉到了一种新的语言和新的思想方式的诞生。 但是读完这本书,你对书中这些蕴含的思想也许需要一种更明晰更系统更透彻的了解和掌握,那么你就...

Global site tag (gtag.js) - Google Analytics