在 cocos2d-x 中实现蒙版支持(一)——使用 CCRenderTexture
在 cocos2d-x 中实现蒙版支持(一)——使用 CCRenderTexture
Get a masked sprite in cocos2d-x use CCRenderTexture
在 cocos2d-x 框架中,并没有为我们提供蒙版支持。想想 AS3 中的 mask 属性,多么地让人怀念啊!
这个系列文章讲解如何在 cocos2d-x 中实现蒙版的支持。
依赖
本文基于 cocos2d-x 2.2.1 和 quick-cocos2d-x 2.2.1 rc 。
系列文章
CCRenderTexture
我们可以将这个类看成 AS3 中的 BitmapData 类。它实现了一块屏幕之外的画布,我们可以利用 OpenGL ES 在上面进行渲染,然后将得到的像素进行处理,并绘制在屏幕上。
实现方案
为了实现遮罩,我们需要两张图片,一张为遮罩图片(mask.png),一张为被遮罩图片(helloworld.jpg)。
mask.png
这张图片中有颜色的部分,根据颜色的 alpha 的值,显示被遮罩图片中对应的像素;没有颜色的部分(alpha为0),则不显示任何被遮罩图片的像素。
helloworld.jpg
最终效果如下:
masked.png
流程如下:
1. 分别创建遮罩图片和被遮罩图片的 CCSprite 对象,统一使用左下角对齐;
1CCSprite* __pMask = CCSprite::create("mask.png");
2__pMask->setAnchorPoint(ccp(0, 0));
3__pMask->setPosition(ccp(0, 0));
4
5CCSprite* __pImg = CCSprite::create("helloworld.jpg");
6__pImg->setAnchorPoint(ccp(0, 0));
7__pImg->setPosition(ccp(0, 0));
2. 创建两个混合模式对象,分别设置给蒙版图像和被蒙版图像;
这两个 ccBlendFunc 实现了以蒙版图像的像素透明度来显示被蒙版图像。具体的含义,可以参考这里: glBlendFunc。
一个更直观的例子和实时预览工具,可以看这里:Visual glBlendFunc + glBlendEquation Tool 。
1ccBlendFunc __maskBF = { GL_ONE, GL_ONE };
2__pMask->setBlendFunc(__maskBF);
3
4ccBlendFunc __imgBF = { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
5__pImg->setBlendFunc(__imgBF);
3. 创建一个 CCRenderTexture 的实例,将其初始化为被遮罩图片的大小;
1CCSize __size = __pImg->getContentSize();
2CCRenderTexture* __pRender = CCRenderTexture::create(__size.width, __size.height);
4. 依次绘制蒙版图像和被蒙版图像,注意是先绘制蒙版图像,再绘制被蒙版图像;
1__pRender->begin();
2__pMask->visit();
3__pImg->visit();
4__pRender->end();
5. 创建一个新的 CCTexture2D 对象,并根据此纹理建立一个 CCSprite ,搞定。
1CCTexture2D* __pTex = new CCTexture2D();
2__pTex->initWithImage(__pRender->newCCImage(true));
3__pTex->autorelease();
4CCSprite* __newSprite = CCSprite::createWithTexture(__pTex);
5this->addChild(__newSprite);
在这一步中,还有一个做法,就是直接从 CCRenderTexture 中包含的 CCSprite 对象中获取 CCTexture2D 对象,然后创建新的 CCSprite:
1CCTexture2D* __pTex = __pRender->getSprite()->getTexture();
2CCSprite* __newSprite = CCSprite::createWithTexture(__pTex);
3__newSprite->filpY(true);
4this->addChild(__newSprite);
但由于得到的纹理是根据Y轴反转过的,我们必须再把它反转回来。
那么上面使用 newCCImage
得到的纹理为什么不用翻转呢?
其实不然, newCCImage(true)
这个调用中的参数 true 就代表需要翻转纹理。
完整代码(C++)
1ccBlendFunc __maskBF = { GL_ONE, GL_ONE };
2ccBlendFunc __imgBF = { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
3
4CCSprite* __pMask = CCSprite::createWithTexture($sprite->getTexture());
5__pMask->setAnchorPoint(ccp(0, 0));
6__pMask->setPosition(ccp(0, 0));
7
8CCSprite* __pImg = CCSprite::createWithTexture($sprite->getTexture());
9__pImg->setAnchorPoint(ccp(0, 0));
10__pImg->setPosition(ccp(0, 0));
11
12__pMask->setBlendFunc(__maskBF);
13__pImg->setBlendFunc(__imgBF);
14
15CCSize __size = __pImg->getContentSize();
16CCRenderTexture* __pRender = CCRenderTexture::create(__size.width, __size.height);
17__pRender->begin();
18__pMask->visit();
19__pImg->visit();
20__pRender->end();
21
22CCTexture2D* __pTex = new CCTexture2D();
23__pTex->initWithImage(__pRender->newCCImage(true));
24__pTex->autorelease();
25CCSprite* __newSprite = CCSprite::createWithTexture(__pTex);
26this->addChild(__newSprite);
完整代码(lua)
1-- @author zrong(zengrong.net)
2-- Creation: 2014-01-21
3function display.newMaskedSprite(__mask, __pic)
4 local __mb = ccBlendFunc:new()
5 __mb.src = GL_ONE
6 __mb.dst = GL_ZERO
7
8 local __pb = ccBlendFunc:new()
9 __pb.src = GL_DST_ALPHA
10 __pb.dst = GL_ZERO
11
12 local __maskSprite = display.newSprite(__mask):align(display.LEFT_BOTTOM, 0, 0)
13 __maskSprite:setBlendFunc(__mb)
14
15 local __picSprite = display.newSprite(__pic):align(display.LEFT_BOTTOM, 0, 0)
16 __picSprite:setBlendFunc(__pb)
17
18 local __maskSize = __maskSprite:getContentSize()
19 local __canva = CCRenderTexture:create(__maskSize.width,__maskSize.height)
20 __canva:begin()
21 __maskSprite:visit()
22 __picSprite:visit()
23 __canva:endToLua()
24
25 local __resultSprite = CCSpriteExtend.extend(
26 CCSprite:createWithTexture(
27 __canva:getSprite():getTexture()
28 ))
29 :flipY(true)
30 return __resultSprite
31end
性能忧虑
在进行了上面的处理之后,CCSprite 其实就变成了一张单独的纹理图片,在显示的时候就和我们从文件中调用的图片一样进行渲染。
这种做法虽然方便,但它会对GPU造成一定的消耗,毕竟合并的工作是实时完成的。GPU在程序运行的过程中帮我们进行了类似 Photoshop 所做的图层合并处理。如果这样的操作过多,肯定会对性能有影响。
在 cocos2d-x 中实现蒙版支持(二)——使用 CCClippingNode(待完成) 一文中,我们会再看看另一种性能更好的实现遮罩的方法。
参考文章
- 文章ID:2060
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/get_a_masked_sprite_in_cocos2d-x_use_ccrendertexture/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。