Qt下的OpenGL 编程(6)混合、雾、抗锯齿

一、提要
 
 混合:对颜色进行混合,实现像“半透明”一样的效果。
 抗锯齿:使直线和多边形的锯齿状边缘变得平滑。
 雾:创建具有大气效果的场景。

二、混合
 
 混合的最终效果是使场景看上去像是半透明的。
 一个通俗的方式来解释混合的话,例如透过绿色的玻璃观察一个物体,我们看到的颜色部分来自于玻璃的绿色,部分来自于
物体的颜色。这两种颜色所占的比例取决于玻璃的传比属性:如果玻璃传播撞击它的光线的80%(即alpha值是0.2),我们看到
的颜色就是玻璃颜色的20%加上玻璃后面那个物体颜色的80%。

 
 
 融合的公式 
 (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))
 这个公式会生成透明/半透明的效果。
 
 混合因子的值位于[0,1]之间。在源颜色和目标颜色进行混合之后,它们的值将进行截取限制在[0,1]的范围之内。
 
 
 在进行三维 混合的时候,多边形的绘图顺序将极大地 影响最终的混合效果。在绘制三维的半透明物体时,根据从前向后还是从后向前绘制多个多边形,最终结果可能大相径庭。正确的混色过程应该是先绘制全部的场景之后再绘制透明的图形。并且要按照与深度缓存相反的次序来绘制(先画最远的物体)。在深度缓存启用时,您应该将透明图形按照深度进行排序,并在全部场景绘制完毕之后再绘制这些透明物体。否则您将得到不正确的结果。
 
 
  
    下面来实现一下混合纹理的木箱。
 首先在nehewiget.h中加入变量isBlend和几个函数:
 public:
 //判断是否启用混合
 bool isBlending();
 //启用混合
 void enableBlend();
 //关闭混合
 void disableBLend();
 protected:
 bool isBlend;

 
 
 函数实现:
 void NeHeWidget::enableBlend()
 {
 this->isBlend=true;
 glEnable(GL_BLEND); // 打开混合
 glDisable(GL_DEPTH_TEST); // 关闭深度测试
 updateGL();
 }
 
 
 void NeHeWidget::disableBLend()
 {
 this->isBlend=false;
 glDisable(GL_BLEND); // 关闭混合
 glEnable(GL_DEPTH_TEST); // 打开深度测试
 updateGL();
 }
 bool NeHeWidget::isBlending()
 {
 return this->isBlend;
 }

 
 
在initializeGL()中加入混合设置:
 
 
// 全亮度, 50% Alpha 混合
     glColor4f(1.0f,1.0f,1.0f,0.5f);
// 基于源象素alpha通道值的半透明混合函数
     glBlendFunc(GL_SRC_ALPHA,GL_ONE);

 
 
加入键盘事件,按键B来控制是否混合:
 
 
case Qt::Key_B:
 if (!neheWidget->isBlending()) neheWidget->enableBlend();
 else neheWidget->disableBLend();
 break;

 
 
最后运行的结果就像这样:
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第1张图片

 
 
三、雾
 有些情况下,添加一些雾可以是图像更加逼真,比如模拟战争,污染,飞行等等。
 插入一下颜色模式的概念:
 
 
  RGBA显示模式
 在RGBA模式中,硬件分配一定数量的位面给R、G、B和A成分(每个成分的数量不一定一样)如图所示。R、G、B的值通常以整型存储,而不是浮点数,并且它们被扩展成可以方便存储和获取的位数。例如,在一个R成分有8位的系统中,从0到255的成分就可以存储,这样,0,1,2,……,255就对应R的值0/255 = 0.0, 1/255, 2/255, ..., 255/255 = 1.0。无论位面的数量是多少,0.0总是最小的亮度值,1.0总是表示最大的亮度值。
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第2张图片

 
 
 颜色索引显示模式
         在颜色索引模式下,OpenGL使用一个颜色表(或查找表),就像用一个调色板来调出场景需要的各种颜色。画家的调色板提供了很多小格子用于调色;类似的,计算机的颜色表提供很多索引,供RGB值进行混合,如下图所示。
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第3张图片

 
 
  模式的选择
 选择RGBA模式还是颜色索引模式
 你要基于可用的硬件和应用程序的需要来决定使用哪种颜色模式。对大多数系统而言,RGBA模式比颜色索引模式能够同时表示更多的颜色。同样,对于大多数效果,例如阴影、光照、纹理映射、雾化,RGBA模式比颜色索引模式提供更加丰富的功能。

 
 
      下面我们继续来实现雾的效果.
 首先在neheWidget.h 中添加一个变量,用于所选择的雾的类型的索引,然后还需要一个函数来变换雾的类型。

 protected:
 GLuint fogFilter;
 public:
 void changeFog();
 
 
 在neheWidget.cpp中添加雾的参数:
 GLuint fogMode[3] = { GL_EXP, GL_EXP2, GL_LINEAR };
 GLfloat fogColor[4] = { 0.5, 0.5, 0.5, 1.0 };

 
 
 变量fogMode,用来保存3种有关雾的类型:GL_EXP,GL_EXP2,GL_LINEAR。这个变量将在代码的开头声明。变量fogColor会保存任何您想要的雾的颜色。

      关于雾的类型
GL_EXP:简单渲染在屏幕上显示的雾的模式。它无法给予我们非常漂亮的雾的效果,但是却可以在古老的电脑上工作的很好。
GL_EXP2:比1提高了一点,将渲染全屏幕的雾,然而她会给予场景更深的效果。
GL_LINEAR:这是最好的雾的渲染模式,对象在雾中消隐的很好。

 
 
 接着在cpp中初始化fogFileter,添加函数实现。
 fogFilter = 0;
 
 
void NeHeWidget::changeFog()
{
 fogFilter+=1;
 if (fogFilter>2) fogFilter=0;
 glFogi( GL_FOG_MODE, fogMode[fogFilter] );
 updateGL();
}

 
 
 
 
在initializeGL()中对雾进行设置:

//选定雾的类型
 glFogi( GL_FOG_MODE, fogMode[fogFilter] );
 //设置雾的颜色
 glFogfv( GL_FOG_COLOR, fogColor );
 //设置雾的浓度
 glFogf( GL_FOG_DENSITY, 0.35 );
 //确定雾的渲染方式
 glHint( GL_FOG_HINT, GL_DONT_CARE );
 //确定了雾的开始初离屏幕有多近
 glFogf( GL_FOG_START, 1.0 );
 //确定了雾的开始初离屏幕有多
 glFogf( GL_FOG_END, 5.0 );
 //开启雾
 glEnable( GL_FOG );

 
 
 关于雾的渲染方式有三个不同的值:
GK_DONT_CARE:让OPENGL自己来确定雾的渲染方式,每顶点或是每像素。

GL_NICEST:对每一像素进行雾的渲染,它看起来是极棒的。
GL_FASTEST:对每一顶点进行雾的渲染,它速度较快,效果就一般了。


 
 
 最后,在mainwindow.cpp中添加键盘事件,来改变雾的效果。
 case Qt::Key_G:
 neheWidget->changeFog();
 break;

 最后的效果像这样:
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第4张图片

 
 
 感觉雾的实现和灯光的实现有些类似,都是先设置参数,然后添加到场景中去。
 实现的原理还是有点复杂,大家有兴趣的话可以翻阅相应的资料。

 
 
四、抗锯齿
 我们在绘制一条1像素的直线的时候,通常它并不是一条直线,
有些地方甚至断掉了,这个就是锯齿,或是走样。抗锯齿又称反走样,在游戏中的效果调整中通常
有多少倍抗锯齿,用的就是这种技术,它可以使图形
的边缘显得更加平滑一些,增加了逼真感,但代价是损失了性能。
 OpenGL中开启抗锯齿有两个步骤:
 启用抗锯齿
 还是以glEnable来启用抗锯齿,可以根据不同图形进行处理
 
 
 GL_POINT_SMOOTH 
 GL_LINE_SMOOTH 线
 GL_POLYGON_SMOOTH 多边形

 
 
 抗锯齿质量
 当然效果越好,那么计算机速度就越慢,即有一个参数设置
 
 
 glHint用于对点,线,多边形的抗锯齿程度进行设置
 
 
 GL_DONT_CARE 放弃,应该是系统默认吧
 GL_FASTEST 速度优先
 GL_NICEST 图形显示质量优先

 
 
 下面用代码来实现一下,回到第二篇那个最初始的框架。
 首先将点和线的大小设大一下,效果会比较明显:

 
 
 glPointSize(20);
 glLineWidth(20);

 
 
      绘制一些点和线:

 
 
void NeHeWidget::paintGL()
{
 // 清除屏幕和深度缓存
 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 glLoadIdentity();
 //坐标转移
 glTranslatef(-1.5f,0.0f,-4.0f);
 //设置颜色
 glColor3f( 1.0, 1.0, 1.0 );
 glBegin(GL_POINTS) ;
 glColor3f(1.0,1.0,1.0); //设置点颜
 glVertex2f(-0.5, -0.5);
 glColor3f(1.0,0.0,0.0);
 glVertex2f(-0.5,0.5);
 glColor3f(0.0,0.0,1.0);
 glVertex2f(0.5,0.5);
 glColor3f(0.0,1.0,0.0);
 glVertex2f(0.5,-0.5);
 glEnd();
 
 
 glLoadIdentity();
 //坐标转移
 glTranslatef(1.5f,0.0f,-4.0f);
 glBegin(GL_LINES);
 glVertex3f(-1.0, 1.0,0.0);
 glVertex3f(1.0,- 1.0,0.0);
 glColor3f(0.0,0.0,1.0);
 glVertex3f(1.0, 1.0,0.0);
 glVertex3f(-1.0, -1.0,0.0);
 
 
 glEnd();
}

 
 
在没有设置抗锯齿的时候,效果是这样的:
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第5张图片

 
 
在initializeGL()中加入抗锯齿的代码:
glEnable (GL_POINT_SMOOTH);
glHint (GL_POINT_SMOOTH, GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

 
 
最后效果是这样:
Qt下的OpenGL 编程(6)混合、雾、抗锯齿_第6张图片

四.参考资料

1.      《 OpenGL Reference Manual 》, OpenGL 参考手册

2.      《 OpenGL 编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波译,机械工业出版社

3.         《win32 OpenGL编程 》   一个大牛的博客     http://blog.csdn.net/vagrxie/article/category/628716/3
4.       《OpenGL函数思考 》   里面有很多OpenGL函数的通俗解释     http://blog.csdn.net/shuaihj
5.       《nehe的OpenGL教程》 比较通俗易懂的OpenGL教程    http://nehe.gamedev.net/

 

你可能感兴趣的:(编程,buffer,qt,Blend,reference,图形)