OpenGL 颜色混合、图元的反走样(五)


颜色混合

 

使用混合的原因:

我们把OpenGL渲染时会把颜⾊值存在颜⾊缓存区中,每个⽚段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜⾊将简单的覆盖原来颜⾊缓存区存在的颜⾊值;当深度缓冲区再次打开时,新的颜⾊⽚段只是当它们⽐原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊⽚段。

那么如果开启深度测试后,当出现2个重叠的图层情况,有⼀个图层是半透明的,另⼀个图层是⾮半透明的,那么此时就不能进⾏单纯的⽐较深度值,然后进⾏覆盖。⽽是需要将2个图层的颜⾊进⾏计算后得到应该显示颜色,这个过程叫“混合”。

blend_1.png

 

通常使用场景:

  • 如果只是单纯的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)

blend_3.jpg

 

修改混合⽅程式:

默认混合⽅程式为: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),而消除这种锯齿的过程就是我们所说的反走样。

sliasing_1.png

 

反走样方式一:

上图所示为两条相交的线,分别是走样的情形和反走样的情形。图像进行了适当放大处理以突出显示效果。右侧宽度为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。通过这种方式,多边形边的像素就可以与之后绘制的其他多边形的颜色很好地融合在一起了。

最后,还需要对场景中的所有多边形进行排序,保证它们按照从前往后的顺序排列后再渲染。

深度缓存对于反走样的使用有一定的负面影响,因为某些像素可能本来需要融混,但是却在深度测试后被抛弃了。如果要保证融混和反走样的正确性,那么我们也许需要禁止深度缓存。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容