cocos2d-x的颜色混合机制依靠OpenGL ES的glBlendFunc()实现,而BlendProtocol和BlendFunc就是对glBlendFunc()的一层封装。
颜色混合机制,顾名思义就是两种或多种颜色叠加在一起时的显示方案。比如可以一种颜色覆盖另一种颜色,可以两种或多种颜色均匀融合,也可以不均匀的融合。这就好像你在向红色颜料中加入蓝色颜料调制紫色的时候,随着蓝色颜料加入量的增多,调制出的颜色依次为:红紫色、紫红色、紫色、紫蓝色、蓝紫色。
cocos2d-x绘制图片有个顺序(zOrder),就好像上面颜料的例子,你得先挤好红色颜料,然后再挤出适量的蓝色颜料进行混合。什么?双管齐下!计算机会say:"NO, NO, NO."的。先画的图片(存储于frame buffer中的图片)的颜色被称为
“目标颜色”,后画的图片(即将要画的图片)的颜色被称为
“源颜色”。当两种颜色叠加时,OpenGL ES会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为
“源因子”,目标颜色乘以的系数称为
“目标因子”),然后相加,这样就得到了新的颜色,而这个源因子和目标因子就是由颜色混合机制决定的。
具体这个叠加颜色是怎么计算出来的,请参见:
http://www.khronos.org/opengles/sdk/docs/man/xhtml/glBlendFunc.xml
然而我觉得这个讲得更通俗易懂些:
http://shahdza.blog.51cto.com/2410787/1547633
BlendProtocol与BlendFunc并无直接继承关系,BlendFunc仅作为对于BlendProtocol的具体配置以结构体的形式出现。而BlendProtocol由于与底层的OpenGL ES关系密切,所以其上也没有父类,它仅作为一个框架类出现,规定了其子类必须实现的东西。
BlendProtocol:
1、成员方法:
(1) virtual void setBlendFunc(const BlendFunc &blendFunc) = 0;
设置使用哪种颜色混合机制。此处为纯虚函数,规定了由子类实现,且必须实现。
(2) virtual const BlendFunc &getBlendFunc() const = 0;
获取当前使用的颜色混合机制。此处为纯虚函数,规定了由子类实现,且必须实现。
BlendFunc:
1、成员变量:
public:
// 源因子。
GLenum src;
// 目标因子。
GLenum dst;
// 源颜色使用全部,目标颜色不使用,进行颜色混合(源颜色覆盖目标颜色),ccTypes.cpp中定义为{GL_ONE, GL_ZERO}。
static const BlendFunc DISABLE;
// 源颜色使用全部,目标颜色使用1-源颜色透明度的值,进行颜色混合,ccTypes.cpp中定义为{GL_ONE, GL_ONE_MINUS_SRC_ALPHA}。
static const BlendFunc ALPHA_PREMULTIPLIED;
// 源颜色使用自身透明度的值,目标颜色使用1-源颜色透明度的值,进行颜色混合,ccTypes.cpp中定义为{GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}。
static const BlendFunc ALPHA_NON_PREMULTIPLIED;
// 源颜色使用自身透明度的值,目标颜色使用全部,ccTypes.cpp中定义为{GL_SRC_ALPHA, GL_ONE}。
static const BlendFunc ADDITIVE;
2、成员方法:
(1) bool operator==(const BlendFunc &a) const
bool operator!=(const BlendFunc &a) const
bool operator<(const BlendFunc &a) const
重载了一些算术符,无他。
实现源码:
bool operator==(const BlendFunc &a) const
{
return src == a.src && dst == a.dst;
}
bool operator!=(const BlendFunc &a) const
{
return src != a.src || dst != a.dst;
}
bool operator<(const BlendFunc &a) const
{
return src < a.src || (src == a.src && dst < a.dst);
}
接下来用一个简单的实例来总结颜色混和机制的使用。
实现源码:
auto box_red = LayerColor::create(Color4B(255, 0, 0, 255));
box_red->setAnchorPoint(Vec2(0.5,0.5));
box_red->setContentSize(Size(300, 300));
box_red->ignoreAnchorPointForPosition(false);
box_red->setPosition(visibleSize.width / 5 * 2, visibleSize.height / 5 * 2);
this->addChild(box_red);
auto box_blue = LayerColor::create(Color4B(0, 0, 255, 255));
box_blue->setAnchorPoint(Vec2(0.5,0.5));
box_blue->setContentSize(Size(300, 300));
box_blue->ignoreAnchorPointForPosition(false);
box_blue->setPosition(visibleSize.width / 5 * 3, visibleSize.height / 5 * 3);
this->addChild(box_blue);
auto box_green = LayerColor::create(Color4B(0, 255, 0, 255));
box_green->setAnchorPoint(Vec2(0.5,0.5));
box_green->setContentSize(Size(300, 300));
box_green->ignoreAnchorPointForPosition(false);
box_green->setPosition(visibleSize.width / 5 * 4, visibleSize.height / 5 * 4);
this->addChild(box_green);
BlendFunc cbl_blue_red = BlendFunc::DISABLE; // 让蓝色覆盖红色。
box_blue->setBlendFunc(cbl_blue_red);
// no effect ↓
BlendFunc cbl_red = BlendFunc::ADDITIVE; // 这段代码虽然写在后面,但仍不会影响上面设置好的蓝色与红色的混合模式,关键点总结中会说明具体原因。
box_red->setBlendFunc(cbl_red);
// no effect ↑
BlendFunc cbl_green_blue = BlendFunc::ADDITIVE; // 让蓝色和绿色混合。
box_green->setBlendFunc(cbl_green_blue);
以上的实例通过LayerColor分别画了3个颜色不同的正方形,分别让他们部分重合,并设置各自的颜色混合模式。
来看看效果:
<img title = 'QQ浏览器截屏未命名.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_410339_0cf6bac63e183f9.png' >
关键点总结:
♂ LayerColor继承自BlendProtocol,它实现了BlendProtocol所规定的setBlendFunc()和getBlendFunc()。
/// blendFunc getter
const BlendFunc &LayerColor::getBlendFunc() const
{
return _blendFunc; // LayerColor::_blendFunc
}
/// blendFunc setter
void LayerColor::setBlendFunc(const BlendFunc &var)
{
_blendFunc = var; // LayerColor::_blendFunc
}
很简单,就是设置变量的值和读取变量的值。之后引擎在渲染界面的过程中,就会使用到LayerColor::_blendFunc。
CCLayer.cpp line 593 LayerColor::onDraw()
|-- CCLayer.cpp line 613 GL::blendFunc( _blendFunc.src, _blendFunc.dst ); // LayerColor::_blendFunc
|-- ccGLStateCache.cpp line 120 blendFunc()
|-- ccGLStateCache.cpp line 130 SetBlending( sfactor, dfactor );
|-- ccGLStateCache.cpp line 107 SetBlending()
static void SetBlending(GLenum sfactor, GLenum dfactor)
{
if (sfactor == GL_ONE && dfactor == GL_ZERO)
{
glDisable(GL_BLEND);
}
else
{
glEnable(GL_BLEND);
glBlendFunc(sfactor, dfactor); // 调用了OpenGL ES的glBlendFunc()。
}
}
♂ 只有对源颜色设置颜色混合模式才能真正起作用,这就是例子中对红色的box设置blendFunc()不起作用的原因。
当红色box被绘制时,红色box作为源颜色,而frame buffer中没有目标颜色,所以红色box的blendFunc()不起任何作用。
当蓝色box被绘制时,frame buffer中的红色box作为目标颜色,所以蓝色box的blendFunc()发挥了作用。
绿色box与蓝色box的混合同理。