找回密码
 立即注册
查看: 1251|回复: 16

[简易教程] 游戏引擎中颜色混合的详解cocos/unity

[复制链接]
发表于 2018-12-17 14:32 | 显示全部楼层 |阅读模式
CCSprite有一个ccBlendFunc类型的blendFunc_结构体成员,可以用来设置描绘时的颜色混合方案。ccBlendFunc包含了一个src和一个dst,分别表示目标和源的运算因子。

    如果我们对一个Sprite使用setBlendFunc方法,如:

             [Sprite setBlendFunc:(ccBlendFunc){GL_ONE,GL_ZERO} ];

    会以这个Sprite作为源,Sprite所在位置的其它像素作为目标,进行混合运算:

        源的RGBA变量:Rs,Gs,Bs,As;

        目标的RGBA:  Rd,Gd,Bd,Ad;

        源的各个运算因子:  N_Rs,N_Gs,N_Bs,N_As;

        目标的各个运算因子:   N_Rd,N_Gd,N_Bd,N_Ad;

混合后的RGBA为:(Rs*N_Rs+ Rd* N_Rd,Gs*N_Gs+ Gd* N_Gd,

                                Bs*N_Bs+ Bd* N_Bd,As*N_As+ Ad* N_Ad)

   其中的运算因子包括:

         GL_ONE:1.0

         GL_ZERO:0.0

         GL_SRC_ALPHA:源的Alpha值作为因子

         GL_DST_ALPHA:目标Alpha作为因子

         GL_ONE_MINUS_SRC_ALPHA:1.0减去源的Alpha值作为因子

         GL_ ONE_MINUS_DST_ALPHA:1.0减去目标的Alpha值作为因子




常数        相关因子        融合因子结果
GL_ZERO        源因子或目的因子        (0,0,0,0)
GL_ONE        源因子或目的因子        (1,1,1,1)
GL_DST_COLOR        源因子        (Rd,Gd,Bd,Ad)
GL_SRC_COLOR        目的因子        (Rs,Gs,Bs,As)
GL_ONE_MINUS_DST_COLOR        源因子        (1,1,1,1)-(Rd,Gd,Bd,Ad)
GL_ONE_MINUS_SRC_COLOR        目的因子        (1,1,1,1)-(Rs,Gs,Bs,As)
GL_SRC_ALPHA        源因子或目的因子        (As,As,As,As)
GL_ONE_MINUS_SRC_ALPHA        源因子或目的因子        (1,1,1,1)-(As,As,As,As)
GL_DST_ALPHA        源因子或目的因子        (Ad,Ad,Ad,Ad)
GL_ONE_MINUS_DST_ALPHA        源因子或目的因子        (1,1,1,1)-(Ad,Ad,Ad,Ad)
GL_SRC_ALPHA_SATURATE        源因子        (f,f,f,1); f=min(As,1-Ad)


以上均为转载,出自http://blog.sina.com.cn/s/blog_7a2ffd5c0100xtid.html

以下为测试的组合效果



代码:

//融合  taget和source都在播放动画,且建立时无添加图片,以下效果均在动画中看到的效果
void mixedSprites(CCSprite* taget, CCSprite* source)
{
    CCRenderTexture* pRT = CCRenderTexture::create(480,320);
    pRT->setPosition(Point(240,160));

    ccBlendFunc func1 = {GL_ZERO, GL_SRC_ALPHA};
    ccBlendFunc func2 = {GL_DST_ALPHA, GL_ZERO};

    taget->setBlendFunc(func1);
    source->setBlendFunc(func2);



    pRT->begin();
    taget->visit();
    source->visit();
    pRT->end();
}



//func1 修改后的组合效果  

ccBlendFunc func1 = {GL_ZERO, GL_ZERO};  taget 变成一个黑色矩形

ccBlendFunc func1 = {GL_ZERO, GL_ONE};   taget 消失

ccBlendFunc func1 = {GL_ZERO, GL_DST_COLOR};   taget 原图片透明,变成高亮区域

ccBlendFunc func1 = {GL_ZERO, GL_SRC_COLOR};  taget 原先透明部分黑色填充

ccBlendFunc func1 = {GL_ZERO, GL_ONE_MINUS_DST_COLOR};  taget  原图不见,成一个模糊矩形

ccBlendFunc func1 = {GL_ZERO, GL_ONE_MINUS_SRC_COLOR};  浮雕样式

ccBlendFunc func1 = {GL_ZERO, GL_SRC_ALPHA};  原图被扣去成透明,原先透明部分黑色填充

ccBlendFunc func1 = {GL_ZERO, GL_ONE_MINUS_SRC_ALPHA};  原图变黑色,原透明部分不变。就像灯光下的影子

ccBlendFunc func1 = {GL_ZERO, GL_DST_ALPHA};  没有图片

ccBlendFunc func1 = {GL_ZERO, GL_ONE_MINUS_DST_ALPHA};  变成一个黑色矩形

ccBlendFunc func1 = {GL_ZERO, GL_SRC_ALPHA_SATURATE};  同上



ccBlendFunc func1 = {GL_ONE, GL_SRC_ALPHA_SATURATE};   原先透明部分黑色填充

ccBlendFunc func1 = {GL_ONE, GL_ONE_MINUS_DST_ALPHA};  同上

ccBlendFunc func1 = {GL_ONE, GL_DST_ALPHA};  原图被蒙上一层白色

ccBlendFunc func1 = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA};  图片周边出现毛刺,被羽化

ccBlendFunc func1 = {GL_ONE, GL_SRC_ALPHA};  原图被蒙上一层白色,透明部分黑色填充

ccBlendFunc func1 = {GL_ONE, GL_ONE_MINUS_SRC_COLOR};  呈半透明,类似飘出的灵魂

ccBlendFunc func1 = {GL_ONE, GL_ONE_MINUS_DST_COLOR};  原先透明部分黑色填充,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_ONE, GL_SRC_COLOR};  原先透明部分黑色填充

ccBlendFunc func1 = {GL_ONE, GL_DST_COLOR};  图片成半透明,显示在一个高亮区域

ccBlendFunc func1 = {GL_ONE, GL_ONE};   呈半透明,类似飘出的灵魂

ccBlendFunc func1 = {GL_ONE, GL_ZERO};  原先透明部分黑色填充



ccBlendFunc func1 = {GL_DST_COLOR, GL_ZERO};  原先透明部分黑色填充,图形成半透明

ccBlendFunc func1 = {GL_DST_COLOR, GL_ONE};   呈半透明,类似飘出的灵魂

ccBlendFunc func1 = {GL_DST_COLOR, GL_DST_COLOR};  图片成半透明,显示在一个高亮区域

ccBlendFunc func1 = {GL_DST_COLOR, GL_SRC_COLOR};  原先透明部分黑色填充

ccBlendFunc func1 = {GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR};  原先透明部分黑色填充,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_DST_COLOR, GL_ONE_MINUS_SRC_COLOR};  图片消失

ccBlendFunc func1 = {GL_DST_COLOR, GL_SRC_ALPHA};   原图成半透明,底色成黑色

ccBlendFunc func1 = {GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA}; 颜色变暗

ccBlendFunc func1 = {GL_DST_COLOR, GL_DST_ALPHA};  呈半透明,类似飘出的灵魂

ccBlendFunc func1 = {GL_DST_COLOR, GL_ONE_MINUS_DST_ALPHA};底色黑色

ccBlendFunc func1 = {GL_DST_COLOR, GL_SRC_ALPHA_SATURATE}; 底色黑色,颜色变暗



ccBlendFunc func1 = {GL_SRC_COLOR, GL_SRC_ALPHA_SATURATE}; 底色黑色,对比度变高

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ONE_MINUS_DST_ALPHA};同上

ccBlendFunc func1 = {GL_SRC_COLOR, GL_DST_ALPHA};呈半透明,类似飘出的灵魂

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA};对比度变高

ccBlendFunc func1 = {GL_SRC_COLOR, GL_SRC_ALPHA}; 原图成半透明,底色成黑色

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR}; 隐身效果

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR}; 原先透明部分黑色填充,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_SRC_COLOR, GL_SRC_COLOR}; 底色黑色,对比度变高

ccBlendFunc func1 = {GL_SRC_COLOR, GL_DST_COLOR};图片成半透明,显示在一个高亮区域

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ONE}; 隐身效果

ccBlendFunc func1 = {GL_SRC_COLOR, GL_ZERO};底色黑色,对比度变高



ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_ZERO};原先透明部分黑色填充,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_ONE}; 隐身效果

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_DST_COLOR};图片成半透明,显示在一个高亮区域

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_SRC_COLOR};底色黑色

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_DST_COLOR};原先透明部分黑色填充,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR};浮雕加隐身效果

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA};原图成半透明,底色成黑色

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA};颜色加深

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_DST_ALPHA};隐身效果

ccBlendFunc func1 = {GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA_SATURATE};原先透明部分黑色填充,之后又全部被蒙上一层颜色



感觉人都傻了。不要问我为什么不会问,我只想说一个字“艹”



ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA_SATURATE};底色黑色, 图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_DST_ALPHA};同上

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_DST_ALPHA};隐身效果,图片糊了

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA};颜色变暗,图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA};原图成半透明,底色成黑色, 图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR};透明,加油画风格

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ONE_MINUS_DST_COLOR};原先透明部分黑色填充,之后又全部被蒙上一层颜色,图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_SRC_COLOR};底色成黑色, 图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR};图片成半透明,显示在一个高亮区域,图片变糊

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ONE};隐身效果,图片糊了

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_COLOR, GL_ZERO};底色成黑色,之后又全部被蒙上一层颜色,图片变糊



求大仙指点迷津,带小人脱离这个凡尘



ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ZERO};底色成黑色

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ONE};隐身效果

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_DST_COLOR};图片成半透明,显示在一个高亮区域

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_SRC_COLOR};底色成黑色,亮度高

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ONE_MINUS_DST_COLOR};底色成黑色,之后又全部被蒙上一层颜色

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR};隐身效果

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_SRC_ALPHA};底色成黑色,原图成半透明

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};原图

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_DST_ALPHA};隐身效果

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA};底色成黑色

ccBlendFunc func1 = {GL_SRC_ALPHA, GL_SRC_ALPHA_SATURATE};底色成黑色



ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA_SATURATE};黑底,只显示图片轮廓

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA};同上

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA};只有图片轮廓,有点白色像粉笔画

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};人物全黑,像灯光下影子

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA};黑底,原先图片处透明

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR};浮雕

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_COLOR};只有图片轮廓,有点白色像粉笔画,再被蒙上一层颜色

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR};黑底加羽化

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_DST_COLOR};白色描边显示在高亮区域

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ONE};白色描边

ccBlendFunc func1 = {GL_ONE_MINUS_SRC_ALPHA, GL_ZERO};黑底,白色描边



ccBlendFunc func1 = {GL_DST_ALPHA, GL_ZERO};黑底

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE};蒙上一层白色

ccBlendFunc func1 = {GL_DST_ALPHA, GL_DST_COLOR};半透明,显示在高亮区域

ccBlendFunc func1 = {GL_DST_ALPHA, GL_SRC_COLOR};黑底,亮度提高

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE_MINUS_DST_COLOR};黑底,亮度提高,再蒙上一层

ccBlendFunc func1 = {GL_DST_ALPHA, GL_SRC_COLOR};黑底,亮度提高

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE_MINUS_DST_COLOR};黑底,亮度提高,再蒙上一层

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE_MINUS_SRC_COLOR};隐身效果

ccBlendFunc func1 = {GL_DST_ALPHA, GL_SRC_ALPHA};黑底,蒙上一层白色

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA};羽化

ccBlendFunc func1 = {GL_DST_ALPHA, GL_DST_ALPHA};隐身效果

ccBlendFunc func1 = {GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA};黑底

ccBlendFunc func1 = {GL_DST_ALPHA, GL_SRC_ALPHA_SATURATE};同上



ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE};黑色矩形

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA};同上

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA};消失

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA};图片变黑

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA};黑底,图片变透明

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_COLOR};浮雕

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_DST_COLOR};黑色矩形

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_SRC_COLOR};黑底,亮度变暗

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_DST_COLOR};高亮框,无图

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ONE};消失

ccBlendFunc func1 = {GL_ONE_MINUS_DST_ALPHA, GL_ZERO};;黑色矩形



ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ZERO};黑色矩形

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ONE};消失

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_DST_COLOR};高亮框,无图

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_SRC_COLOR};黑底,亮度变暗

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_DST_COLOR};黑色矩形

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_COLOR};浮雕

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_SRC_ALPHA};图片变透明,留黑底

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA};人物全黑,像灯光下影子

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_DST_ALPHA};消失

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_DST_ALPHA};黑色矩形

ccBlendFunc func1 = {GL_SRC_ALPHA_SATURATE, GL_SRC_ALPHA_SATURATE};同上



终于结束了。

没文化很可怕,没智商更可怕。以上就是很好的例子。
---------------------
作者:凡人道
来源:CSDN
原文:https://blog.csdn.net/daydayup_chf/article/details/18405131
版权声明:本文为博主原创文章,转载请附上博文链接!

 楼主| 发表于 2018-12-17 14:50 | 显示全部楼层
glBlendFunc颜色混合
混合是什么呢?混合就是把两种颜色混在一起。具体一点,就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。
假设我们需要绘制这样一个场景:透过红色的玻璃去看绿色的物体,那么可以先绘制绿色的物体,再绘制红色玻璃。在绘制红色玻璃的时候,利用“混合”功能,把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色,看上去就好像玻璃是半透明的。
要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。
注意:只有在RGBA模式下,才可以使用混合功能,颜色索引模式下是无法使用混合功能的。
一、源因子和目标因子
前面我们已经提到,混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理后得到一种新的颜色。这里把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。
OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜 色。(也可以不是相加,新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨 论这个了)
下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs,  As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db,  Da)。则混合产生的新颜色可以表示为:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。

源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。
GL_ZERO:     表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE:      表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。
除 此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为 因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、 GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。但这些都不是我们现在所需要了解的。毕竟这还是入门教材,不需要整得太复杂~

举例来说:

(将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。)
如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
如 果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。
注意:
所 谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则 红色就是源颜色,绿色才是目标颜色。在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕 了。



三、实现三维混合
也许你迫不及待的想要绘制一个三维的带有半透明物体的场景了。但是现在恐怕还不行,还有一点是在进行三维场景的混合时必须注意的,那就是深度缓冲。
深 度缓冲是这样一段数据,它记录了每一个像素距离观察者有多近。在启用深度缓冲测试的情况下,如果将要绘制的像素比原来的像素更近,则像素将被绘制。否则, 像素就会被忽略掉,不进行绘制。这在绘制不透明的物体时非常有用——不管是先绘制近的物体再绘制远的物体,还是先绘制远的物体再绘制近的物体,或者干脆以 混乱的顺序进行绘制,最后的显示结果总是近的物体遮住远的物体。
然而在你需要实现半透明效果时,发现一切都不是那么美好了。如果你绘制了一个近距离的半透明物体,则它在深度缓冲区内保留了一些信息,使得远处的物体将无法再被绘制出来。虽然半透明的物体仍然半透明,但透过它看到的却不是正确的内容了。
要 解决以上问题,需要在绘制半透明物体时将深度缓冲区设置为只读,这样一来,虽然半透明物体被绘制上去了,深度缓冲区还保持在原来的状态。如果再有一个物体 出现在半透明物体之后,在不透明物体之前,则它也可以被绘制(因为此时深度缓冲区中记录的是那个不透明物体的深度)。以后再要绘制不透明物体时,只需要再 将深度缓冲区设置为可读可写的形式即可。嗯?你问我怎么绘制一个一部分半透明一部分不透明的物体?这个好办,只需要把物体分为两个部分,一部分全是半透明 的,一部分全是不透明的,分别绘制就可以了。
即使使用了以上技巧,我们仍然不能随心所欲的按照混乱顺序来进行绘制。必须是先绘制不透明的物体,然 后绘制透明的物体。否则,假设背景为蓝色,近处一块红色玻璃,中间一个绿色物体。如果先绘制红色半透明玻璃的话,它先和蓝色背景进行混合,则以后绘制中间 的绿色物体时,想单独与红色玻璃混合已经不能实现了。
总结起来,绘制顺序就是:首先绘制所有不透明的物体。如果两个物体都是不透明的,则谁先谁后 都没有关系。然后,将深度缓冲区设置为只读。接下来,绘制所有半透明的物体。如果两个物体都是半透明的,则谁先谁后只需要根据自己的意愿(注意了,先绘制 的将成为“目标颜色”,后绘制的将成为“源颜色”,所以绘制的顺序将会对结果造成一些影响)。最后,将深度缓冲区设置为可读可写形式。
调用glDepthMask(GL_FALSE);可将深度缓冲区设置为只读形式。调用glDepthMask(GL_TRUE);可将深度缓冲区设置为可读可写形式。
一 些网上的教程,包括大名鼎鼎的NeHe教程,都在使用三维混合时直接将深度缓冲区禁用,即调用glDisable(GL_DEPTH_TEST);。这样 做并不正确。如果先绘制一个不透明的物体,再在其背后绘制半透明物体,本来后面的半透明物体将不会被显示(被不透明的物体遮住了),但如果禁用深度缓冲, 则它仍然将会显示,并进行混合。NeHe提到某些显卡在使用glDepthMask函数时可能存在一些问题,但可能是由于我的阅历有限,并没有发现这样的 情况。

那么,实际的演示一下吧。我们来绘制一些半透明和不透明的球体。假设有三个球体,一个红色不透明的,一个绿色半透明的,一个蓝色半透明的。红色最远,绿色 在中间,蓝色最近。根据前面所讲述的内容,红色不透明球体必须首先绘制,而绿色和蓝色则可以随意修改顺序。这里为了演示不注意设置深度缓冲的危害,我们故 意先绘制最近的蓝色球体,再绘制绿色球体。
为了让这些球体有一点立体感,我们使用光照。在(1, 1, -1)处设置一个白色的光源。代码如下:
void setLight(void)
{
    static const GLfloat light_position[] = {1.0f, 1.0f, -1.0f, 1.0f};
    static const GLfloat light_ambient[]  = {0.2f, 0.2f, 0.2f, 1.0f};
    static const GLfloat light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};
    static const GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    glEnable(GL_DEPTH_TEST);
}
每一个球体颜色不同。所以它们的材质也都不同。这里用一个函数来设置材质。
void setMatirial(const GLfloat mat_diffuse[4], GLfloat mat_shininess)
{
    static const GLfloat mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
    static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    glMaterialfv(GL_FRONT, GL_EMISSION,  mat_emission);
    glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);
}
有了这两个函数,我们就可以根据前面的知识写出整个程序代码了。这里只给出了绘制的部分,其它部分大家可以自行完成。
void myDisplay(void)
{
    // 定义一些材质颜色
    const static GLfloat red_color[] = {1.0f, 0.0f, 0.0f, 1.0f};
    const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f};
    const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f};

    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 启动混合并设置混合因子
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 设置光源
    setLight();

    // 以(0, 0, 0.5)为中心,绘制一个半径为.3的不透明红色球体(离观察者最远)
    setMatirial(red_color, 30.0);
    glPushMatrix();
    glTranslatef(0.0f, 0.0f, 0.5f);
    glutSolidSphere(0.3, 30, 30);
    glPopMatrix();

    // 下面将绘制半透明物体了,因此将深度缓冲设置为只读
    glDepthMask(GL_FALSE);

    // 以(0.2, 0, -0.5)为中心,绘制一个半径为.2的半透明蓝色球体(离观察者最近)
    setMatirial(blue_color, 30.0);
    glPushMatrix();
    glTranslatef(0.2f, 0.0f, -0.5f);
    glutSolidSphere(0.2, 30, 30);
    glPopMatrix();

    // 以(0.1, 0, 0)为中心,绘制一个半径为.15的半透明绿色球体(在前两个球体之间)
    setMatirial(green_color, 30.0);
    glPushMatrix();
    glTranslatef(0.1, 0, 0);
    glutSolidSphere(0.15, 30, 30);
    glPopMatrix();

    // 完成半透明物体的绘制,将深度缓冲区恢复为可读可写的形式
    glDepthMask(GL_TRUE);

    glutSwapBuffers();
}

大家也可以将上面两处glDepthMask删去,结果会看到最近的蓝色球虽然是半透明的,但它的背后直接就是红色球了,中间的绿色球没有被正确绘制。

小结:
本课介绍了OpenGL混合功能的相关知识。
混合就是在绘制时,不是直接把新的颜色覆盖在原来旧的颜色上,而是将新的颜色与旧的颜色经过一定的运算,从而产生新的颜色。新的颜色称为源颜色,原来旧的颜色称为目标颜色。传统意义上的混合,是将源颜色乘以源因子,目标颜色乘以目标因子,然后相加。
源 因子和目标因子是可以设置的。源因子和目标因子设置的不同直接导致混合结果的不同。将源颜色的alpha值作为源因子,用1.0减去源颜色alpha值作 为目标因子,是一种常用的方式。这时候,源颜色的alpha值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。
在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。
在进行三维混合时,不仅要考虑源因子和目标因子,还应该考虑深度缓冲区。必须先绘制所有不透明的物体,再绘制半透明的物体。在绘制半透明物体时前,还需要将深度缓冲区设置为只读形式,否则可能出现画面错误


---------------------------------------------------------------------------------

关于透明,OpenGL/ES 中可以通过 blend (混色) 来简单实现,混色的基本原理就是把要绘制的物体的颜色与屏幕上已经绘制好的颜色以一定比例来混合,最后的颜色看上去就像半透明一样。

要使用混合先要通过 glEnable 函数来启用

?
1
glEnable(GL10.GL_BLEND);
然后通过 glBlendFunc 来设置下要使用的混合方法

?
1
glBlendFunc(sfactor, dfactor);
sfactor 及 dfactor 分别代表源和目标颜色在混合时所占比重的枚举常量。其中 sfactor 可取值包括:GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE ; 而 dfactor 可取值包括:GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA。

以下说明及公式中所涉及颜色的R、G、B、A值都是指浮点形式,即范围在 0.0f – 1.0f 之间。

在计算混色时,首先是根据以上的枚举得出源颜色和目标颜色的系数,然后分别与源和目标颜色相乘(乘积大于1时取值1),然后再把得出的结果相加。那么如果假设根据以上枚举得出的RGBA四个成分上的系数分别为 源 sfR, sfG, sfB, sfA, 目标 dfR, dfG, dfB, dfA, 源和目标的颜色成分值分别用 sR, sG, sB, sA 和 dR, dG, dB, dA 表示的话, 最终结果色的 rR, rG, rB, rA 分别为:

rR = sR * sfR + dR * dfR
rG = sG * sfG + dG * dfG
rB = sB * sfB + dB * dfB
rA = sA * sfA + dA * dfA

以上 rR, rG, rB, rA 的值如果大于 1 则最终取值为 1 。

然后再来看看 xfR, xfG, xfB, xfA ( x => s / d )是怎么得出来的。

factor枚举        xfR, xfG, xfB, xfA
GL_ZERO        0, 0, 0, 0
GL_ONE        1, 1, 1, 1
GL_SRC_COLOR        sR, sG, sB, sA
GL_DST_COLOR        dR, dG, dB, dA
GL_ONE_MINUS_SRC_COLOR        1-sR, 1-sG, 1-sB, 1-sA
GL_ONE_MINUS_DST_COLOR        1-dR, 1-dG, 1-dB, 1-dA
GL_SRC_ALPHA        sA, sA, sA, sA
GL_DST_ALPHA        dA, dA, dA, dA
GL_ONE_MINUS_SRC_ALPHA        1-sA, 1-sA, 1-sA, 1-sA
GL_ONE_MINUS_DST_ALPHA        1-dA, 1-dA, 1-dA, 1-dA
GL_SRC_ALPHA_SATURATE        i, i, i, 1 *
上边的表中最后的 i 取值为 min ( sA, 1-dA )

根据以上公式,

glBlendFunc(GL_ONE, GL_ONE); 即源与目标颜色的RGBA分别相加。

glBlendFunc(GL_ONE, GL_ZERO); 即只取源颜色,这也是默认值。

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); 是比较典型的半透明效果,如果源色 alpha 为0,则取目标色,如果源色alpha为1,则取源色,否则视源色的alpha大小各取一部分。源色的alpha越大,则源色取的越多,最终结果源色的表现更强;源色的alpha越小,则目标色“透过”的越多。

此外在一般的渲染过程中,都会把有半透明效果的渲染放到后边,先把不透明的部分在深度测试启用的情况下渲染完, 再关闭深度测试写入(glDepthMask(false)),并渲染半透明的部分。这样就不会出现由于半透明且离镜头近的面被先渲染时污染深度缓冲了。
发表于 2019-6-24 08:15 | 显示全部楼层
很不错
发表于 2019-6-24 07:41 | 显示全部楼层
好帖就是要顶
发表于 2019-6-24 07:56 | 显示全部楼层
真心顶
发表于 2019-6-24 07:53 | 显示全部楼层
很好哦
发表于 2019-6-24 08:01 | 显示全部楼层
LZ真是人才
发表于 2020-4-15 08:51 | 显示全部楼层
顶顶多好
发表于 2020-4-15 08:23 | 显示全部楼层
真心顶
发表于 2020-4-15 08:50 | 显示全部楼层
说的非常好
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-9 23:33 , Processed in 0.131940 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表