颜色混合
使用混合的原因:
我们把OpenGL渲染时会把颜⾊值存在颜⾊缓存区中,每个⽚段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜⾊将简单的覆盖原来颜⾊缓存区存在的颜⾊值;当深度缓冲区再次打开时,新的颜⾊⽚段只是当它们⽐原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊⽚段。
那么如果开启深度测试后,当出现2个重叠的图层情况,有⼀个图层是半透明的,另⼀个图层是⾮半透明的,那么此时就不能进⾏单纯的⽐较深度值,然后进⾏覆盖。⽽是需要将2个图层的颜⾊进⾏计算后得到应该显示颜色,这个过程叫“混合”。
通常使用场景:
- 如果只是单纯的2个图层重叠时进⾏混合,只需要开启混合,设置混合方式(设置后在固定着⾊器或可编程着⾊器中它会自动混合)。
- 比如一些滤镜效果的制作,需要在可编程管线的⽚元着⾊器的着色时,套⽤公式或自定义进⾏颜⾊混合⽅程式计算,得到新的颜色值。例如将处理图⽚原图颜⾊加上薄薄的绿⾊,可以得到更好的视觉效果。
混合的具体使用:
⽬标颜⾊: 已经存储在颜⾊缓存区的颜⾊值。
源颜⾊: 作为当前渲染命令结果进⼊颜⾊缓存区的颜⾊值。
// 在OpenGL中默认是关闭的,需要开启GL_BLEND来启用混合功能:
glEnable(GL_BlEND);
// 设置混合因⼦。S:源混合因⼦;D:⽬标混合因⼦。
glBlendFunc(GLenum S,GLenum D);
OpenGL混合计算方程式:
开启混合后,我们通过glBlendFunc函数告诉OpenGL使用什么混合因子来进行计算,而默认计算方程式是固定的,只需要改变因子来变换计算参数,得到不同的使用效果。
// 当混合功能被启动时,源颜⾊和⽬标颜⾊的组合⽅式是混合⽅程式控制的。
// 在默认情况下,混合⽅程式如下所示:
Cf = (Cs * S) + (Cd * D)
Cf :最终计算参数的颜⾊
Cs : 源颜⾊
Cd :⽬标颜⾊
S:源混合因⼦
D:⽬标混合因⼦
OpenGL混合因子表:
表中表示的都是计算方程式中参数的计算规则。
表中R、G、B、A 分别代表 红、绿、蓝、alpha。
表中下标S、D,分别代表源、⽬标。
表中C 代表常量颜⾊(默认⿊⾊)。
混合计算例子:
下⾯通过⼀个常⻅的混合函数组合来说明问题:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
如果颜⾊缓存区已经有⼀种颜⾊红⾊(1.0f,0.0f,0.0f,1.0f),这个⽬标颜⾊Cd;
如果在这上⾯⽤⼀种alpha为0.6的蓝⾊(0.0f,0.0f,1.0f,0.6f)。
Cd (⽬标颜⾊) = (1.0f,0.0f,0.0f,1.0f);
Cs (源颜⾊) = (0.0f,0.0f,1.0f,0.6f);
S = 源alpha值 = 0.6f
D = 1 - 源alpha值= 1-0.6f = 0.4f
⽅程式 Cf = (Cs * S) + (Cd * D)
等价于 = (Blue * 0.6f) + (Red * 0.4f)
修改混合⽅程式:
默认混合⽅程式为:Cf = (Cs * S) + (Cd * D)
实际上远不⽌这⼀种混合⽅程式,我们可以从5个不同的⽅程式中进⾏选择:
//选择混合⽅程式的函数:
glbBlendEquation(GLenum mode);
模式 | 函数 |
---|---|
GL_FUNC_ADD | Cf = (Cs * S) + (Cd * D) |
GL_FUNC_SUBTRACT | Cf = (Cs * S) - (Cd * D) |
GL_FUNC_REVERSE_SUBTRACT | Cf = (Cd * D) - (Cs * S) |
GL_MIN | Cf = min (Cs, Cd) |
GL_MAX | Cf = max (Cs, Cd) |
修改常量混合颜⾊:
在混合因⼦表中,GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT值允许混合⽅程式中引⼊⼀个常量混合颜⾊。默认初始化为⿊⾊(0.0f,0.0f,0.0f,1.0f),但是还是可以修改这个常量混合颜⾊。
常量混合颜⾊,默认初始化为⿊⾊(0.0f,0.0f,0.0f,1.0f),但是还是可以修改这个常量混合颜⾊。
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclam pf alpha );
设置混合因子:
- glBlendFunc函数可以指定源和⽬标RGBA值的混合因子。
- glBlendFuncSeparate函数则允许为RGB和Alpha成分单独指定混合因子。
glBlendFunc (GLenum sfactor, GLenum dfactor)
sfactor:源混合因⼦;
dfactor:⽬标混合因⼦。
// 相对glBlendFunc来设置混合因子,glBlendFuncSeparate更灵活:
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);
strRGB: 源颜色的混合因子
dstRGB: ⽬标颜色的混合因子=
strAlpha: 源颜色的Alpha因子
dstAlpha: ⽬标颜色的Alpha因子
反走样
为什么走样:
我们都知道,在数学的定义中一条线段是由两个端点确定的,而线段是没有宽度和面积的。但在计算机图形领域中,为了让人的肉眼能够看到,必须给线段一定的宽度,所以我们的线段通常是由两个端点和一个宽度参数确定的,而我们计算机中图形的宽度通常都是以像素为单位的,因此我们的线段宽度有可能是1像素也有可能是n像素。
使用OpenGL进行渲染后,我们很容易发现渲染图像的线存在锯齿问题。之所以产生这种锯齿,是因为一条理想的直线是通过像素网格上的一系列像素点去逼近的。由此产生的锯齿问题叫做走样(sliasing),而消除这种锯齿的过程就是我们所说的反走样。
反走样方式一:
上图所示为两条相交的线,分别是走样的情形和反走样的情形。图像进行了适当放大处理以突出显示效果。右侧宽度为1个像素的对角线段覆盖了比一般线段更多的像素块。事实上,当我们实现反走样的时候,OpenGL会根据屏幕上每个像素块所覆盖的范围来计算每个片元的覆盖值。OpenGL会将片元alpha值与这个覆盖值相乘。然后我们就可以使用alpha值来实现片元与帧缓存中已有像素的融混操作了。
覆盖值计算的细节是非常复杂的,很难概要地进行讲述。事实上,不同的 OpenGL实现中的计算方法也是存在细微差别的。我们可以使用gIHint()来进行进一步的控制,在图像质量和速度上做出权衡,不过并不是所有的设备实现都会受到这个函数的影响。
// glHint控制OpenGL的一些具体特性。
// hint参数的解析方式是与平台相关的,有些OpenGL的实现可能会完全忽略它们的影响。
void glHint(GLenum target, GLenum hint);
glHint的参数target用来设置要控制的特性类型:
target参数 | 描述 |
---|---|
GL_POINT_SMOOTH_HINT | 点的反走样质量 |
GL_LINE_SMOOTH_HINT | 线的反走样质量 |
GL_POLYGON_SMOOTH_HINT | 多边形边的反走样质量 |
GL_TEXTURE_COMPRESSION_HINT | 纹理图像压缩的质量和性能 |
GL_FRAGMENT_SHADER_DERIVATIVE_HINT | 片元处理内置函数的导数精度,包括 dFdx、dFdx 以及 fwidth |
glHint的参数hint用来设置要控制的质量最高类型:
hint参数 | 描述 |
---|---|
GL_FASTEST | 使用效率最高的方式 |
GL_NICEST | 使用质量最高的方式 |
GL_DONT_CARE | 表示没有偏好 |
反走样方式二:
如果程序中十分需要多重采样的效果,那么对线段和多边形反走样的另一种方法就是通过gIEnable()开启反走样,然后将GL_LINE_SMOOTH或者 GL_POLYGON_SMOOTH作为参数传入。你可能也需要通过gIHint()来设置一个质量参考值。
点的反走样:
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH,GL_NICEST);
线段的反走样:
首先,需要开启融混。融混参数通常可以设置为 GL_SRC_ALPHA(源)和GL_ONE_MINUS_SRC_ALPHA(目标)。或者也可以使用GL_ONE作为目标参数,这样线段相交的地方会显得更亮。现在我们可以使用反走样的方式来绘制点和线段了。如果用的alpha值足够高,那么反走样效果是非常明显的。注意当你设置使用融混时,需要考虑好渲染的顺序。不过大多数情况下,忽略顺序也不会带来很明显的问题。
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
多边形的反走样:
glEnable(GL_POLYGON_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
填充多边形的边的反走样与线段的反走样过程相似。由于不同的多边形的边之间存在着交叠,因此我们需要适当地对颜色值进行融混。
为了对多边形进行反走样,需要使用alpha值来表达多边形边的覆盖值。通过glIEnable()设置参数GL_POLYGON_SMOOTH来开启多边形的反走样功能。这样多边形边上的像素就会根据覆盖率来设置alpha值,这一点与线段的反走样是一致的。当然,如果需要的话,我们也可以设置 GL_POLYGON_SMOOTH_HINT的质量值。
为了确保边的融混方式恰当,我们可以设置颜色混合参数为 GL_SRC_ALPHA_SATURATE(源)和 GL_ONE(目标)。通过这个特定的融混函数,得到的最终颜色将是目标颜色和成比例的源颜色的总和;比例系数其实就是以下两者中的较小者:输入源的alpha值、1减去目标alpha值。这也就是说,对于一个alpha值很大的像素,连续输入像素对于最终颜色的影响很小,因为1减去目标alpha几乎为0。通过这种方式,多边形边的像素就可以与之后绘制的其他多边形的颜色很好地融合在一起了。
最后,还需要对场景中的所有多边形进行排序,保证它们按照从前往后的顺序排列后再渲染。
深度缓存对于反走样的使用有一定的负面影响,因为某些像素可能本来需要融混,但是却在深度测试后被抛弃了。如果要保证融混和反走样的正确性,那么我们也许需要禁止深度缓存。