OpenGL编程指南第六章:混合、反锯齿、雾、多边形偏移

来源:互联网 发布:log4j.xml 配置 sql 编辑:程序博客网 时间:2024/05/29 02:22

1、混合(blending)

混合发生在图元光栅化之后,片段(fragment)写入frameBuffer之前;片段与对应位置的frameBuffer像素进行互操作形成新的像素颜色的过程及时混合。需要通过glEnable(GL_BLEND)来激活混合功能,否则的话fragmen直接覆盖对应像素。

颜色的alpha分量值此时就会发挥作用。

混合因子(blend factor)

混合操作的时候,fragment颜色叫做源source,像素颜色叫做目标destination。源与目标进行运算之前要乘以一个因子,分别是:Sr,Sg,Sb,Sa和Dr,Dg,Db,Da,混合操作的公式可表示为:(Rs*Sr+Rd*Dr,Gs*Sg+Gd*Dg,Bs*Sb+Bd*Db,As*Sa+Ad*Da)。最终计算结果会被钳制在[0,1]范围内。

有两个API可以用来设定这个因子:glBlendFunc(GLenum srcfactor, GLenum destfactor)和glBlendFuncSeparate(GLenum srcRGB, GLenum destRGB,GLenum srcAlpha, GLenum destAlpha),后者的区别就是可以对alpha分量单独设置。参数是一些枚举值,具体的参数值列表及其含义请参考原书。

在设定因子的时候,某些带CONSTANT字样的参数值,需要一个另外的颜色常量来参与计算,这个常量通过glBlendColor(GLclampf red, GLclampf green, GLclampf blue,GLclampf alpha)来指定。

混合方程(blend equation)

上一部分的混合使用的算数操作是加法,这个也是可以改变的,glBlendEquation(GLenum mode);glBlendEquationSeparate(GLenum modeRGB,GLenum modeAlpha)可以设置算数操作,这两个API的区分一目了然。具体的参数请参考原书。

混合用例

这一部分描述了一些使用blend来达到某种效果的用例,通常一种效果可以通过多种方式来达到:

1)均匀混合两个图像:先将源factor设为GL_ONE,目标factor设为GL_ZERO,将第一个图绘制到frame buffer;再将源factor设为GL_SRC_ALPHA,目标factor设为GL_ONE_MINUS_ALPHA,将第二个图以alpha为0.5绘制到frame buffer。通过调节alpha的值,可以控制两个图像的比重。

2) 均匀混合三个图像:清空framebuffer,将源factor设为0.333333333,目标factor设为GL_SRC_ALPHA,再依次以alpha为0.5绘制三个图像。

3) 过滤效果:将源 factor设为GL_DST_COLOR 或GL_ONE_MINUS_DST_COLOR,将目标factor设为GL_SRC_COLOR 或GL_ONE_MINUS_SRC_COLOR可以实现对图像颜色各分量分别实现调节。(我的理解:)比如先将frame buffer清空为(0.4,0.3,0.2,1.0),再分别将源、目标factor设置为GL_DST_COLOR和GL_SRC_COLOR,那么绘制效果是red 80%,green 60%,blue 40%。

4) 非矩形的光栅化:可以通过混合对图像进行非规则的光栅化,比如你可以绘制一个任意形状的多边形,将期内部颜色alpha设为1.0,外部颜色alpha设为0,在将图像绘制上去,通过控制参数,就可以达到只在多边形内部显示图像的目的。billboarding技术就是以这个为基础的。

5)反锯齿:这个在后面会讲到。

混合和顺序

两个相互重叠的透明物体最终呈现出的视觉效果,与他们绘制的顺序是有关的。

在3维模型下,如果开启DepthBuffer,如果半透明的物体离视点更近的话,glDepthMask(GL_FALSE)可以把depth buffer暂时变成readonly的,所以在绘制半透明物体的时候应该这样做,这样半透明后面的物体能够有机会绘制。

但无论如何,我觉得绘制的顺序会影响最终的视觉效果。

2、反锯齿

屏幕本质上是一个像素矩阵,像素毕竟是具有一定大小的方格,那么表示一条直线一组像素不可能严格在一条直线上。仔细观察屏幕上的直线,会发现有锯齿的效果,尤其当直线近似竖直或近似平放的时候。

直线、点、多边形进行反锯齿
使用glEnable(GL_POINT_SMOOTH)或glEnable(GL_LINE_SMOOTH)或glEnable(GL_POLYGON_SMOOTH)开启该功能。该功能的实现方式是,对于直线经过的像素点,计算一个coverage值,表示直线对该像素点的覆盖度,在执行blend之前,fragment的alpha值会先乘上coverage值。从这可知道,反锯齿是基于blend的。

多重采样(multisampling)反锯齿
多重采样是通过额外的缓冲区和计算来进行反锯齿操作的。具体说来,每个像素点包含多个采样点,opengl会保存fragment在每个采样点的颜色、深度、stencil信息,最终fragement在该像素点coverage值由这采样点的信息计算得来。
使用多重采样的步骤:1)glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB |GLUT_MULTISAMPLE),初始化的时候开启;2)glGetIntegerv(GL_SAMPLE_BUFFERS, &bufs);glGetIntegerv(GL_SAMPLES, &samples);可以检查多重采样是否可用,前者返回1,后者返回大于1表示可用; 3)glEnable(GL_MULTISAMPLE),可以激活。

3、雾(frog)

通过使用雾,能够达到“物体随着远离视点而淡出视线”的效果。

使用雾先要调用glEnable(GL_FOG);可以设置雾的颜色glFogfv(GL_FOG_COLOR, fogColor);雾的浓度glFogf(GL_FOG_DENSITY, 0.5);雾的范围glFogf(GL_FOG_START, 1.0),glFogf(GL_FOG_END, 5.0),参数值代表离视点的距离;雾的模式glFogi(GL_FOG_MODE, fogMode)。

计算公式

opengl会混合fragment的颜色和雾的颜色,混合之前要计算一个frog混合因子:

z:默认表示fragment的深度,也可以通过API来主动设定;
模式:GL_EXP,GL_EXP2,GL_LINEAR是三种模式,每种模式下f的计算方式不同
density:雾的浓度
f的值会被限制在[0,1]以内。

f计算好了之后,加上雾效果的fragment颜色计算为:

Cf:雾颜色
Ci:fragment的原始颜色

雾坐标

上文中雾坐标默认使用fragment的深度,但也可以通过API设置:glFog(GL_FOG_COORD_SRC, GL_FOG_COORD)设置雾坐标来源于手动设定;glFogCoord*()设定顶点的雾坐标。

4、点参数

有时候我们想通过点来表示小的原形或球形物体,而不是通过复杂、低效的多边形模型,比如灯、水滴等。
glPointSize() 和glEnable(GL_POINT_SMOOTH)可以产生各种尺寸的、圆形的点。
glPointParameterf{if}{v}()可以指定更多有用的点参数:
GL_POINT_DISTANCE_ATTENUATION指定一组常量,来控制点随着远离视点,其尺寸衰减方式;
GL_POINT_SIZE_MIN和GL_POINT_SIZE_MAX可以将衰减控制在一个范围内;
GL_POINT_FADE_THRESHOLD_SIZE指定一个常量size值,当点的尺寸衰减到小于这个size时,其alpha值会按比例衰减;
GL_POINT_SPRITE_COORD_ORIGIN指定点的纹理坐标起点,GL_LOWER_LEFT或GL_UPPER_LEFT。

5、多边形偏移(polygon offset)

如果想加亮一个物体的边界,可能采取的措施是先以GL_FILL模式来画(组成物体表面的)多边形,再以GL_LINE模式来画同一个多边形。但是不同模式的多边形并不是按相同的方式进行光栅化的,产生的深度(depth)值也不一定完全一致,因此可能的结果是加亮的边界时隐时现,断断续续。
解决这个问题的一个方式就是polygon offset,它给多边形施加一个深度坐标的偏移。
glEnable() GL_POLYGON_OFFSET_FILL, GL_POLYGON_OFFSET_LINE,GL_POLYGON_OFFSET_POINT来激活不同模式的polygon offset;
glPolygonOffset(GLfloat factor, GLfloat units)来设定偏移参数:
offset的计算方法是offset = m * factor + r * units,m是深度斜率(depth slop),由系统计算;r是一个系统预定义的值用来产生有意义的最小的深度偏差。
合理的参数值随具体的情况而定,线的宽度、深度值、深度斜率都会产生影响。

原创粉丝点击