您的位置:首页 > 编程语言 > Qt开发

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

2014-11-05 14:44 218 查看
一、提要
混合:对颜色进行混合,实现像“半透明”一样的效果。
抗锯齿:使直线和多边形的锯齿状边缘变得平滑。
雾:创建具有大气效果的场景。
二、混合
混合的最终效果是使场景看上去像是半透明的。
一个通俗的方式来解释混合的话,例如透过绿色的玻璃观察一个物体,我们看到的颜色部分来自于玻璃的绿色,部分来自于
物体的颜色。这两种颜色所占的比例取决于玻璃的传比属性:如果玻璃传播撞击它的光线的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;
最后运行的结果就像这样:
三、雾
有些情况下,添加一些雾可以是图像更加逼真,比如模拟战争,污染,飞行等等。
插入一下颜色模式的概念:
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总是表示最大的亮度值。
颜色索引显示模式
在颜色索引模式下,OpenGL使用一个颜色表(或查找表),就像用一个调色板来调出场景需要的各种颜色。画家的调色板提供了很多小格子用于调色;类似的,计算机的颜色表提供很多索引,供RGB值进行混合,如下图所示。
模式的选择
选择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;
最后的效果像这样:
感觉雾的实现和灯光的实现有些类似,都是先设置参数,然后添加到场景中去。
实现的原理还是有点复杂,大家有兴趣的话可以翻阅相应的资料。
四、抗锯齿
我们在绘制一条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();
}
在没有设置抗锯齿的时候,效果是这样的:
在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);
最后效果是这样:

四.参考资料

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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: