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 对象,统一使用左下角对齐;
1
2
3
4
5
6
7
|
CCSprite
*
__pMask
=
CCSprite
::
create
(
"mask.png"
)
;
__pMask
->
setAnchorPoint
(
ccp
(
0
,
0
)
)
;
__pMask
->
setPosition
(
ccp
(
0
,
0
)
)
;
CCSprite
*
__pImg
=
CCSprite
::
create
(
"helloworld.jpg"
)
;
__pImg
->
setAnchorPoint
(
ccp
(
0
,
0
)
)
;
__pImg
->
setPosition
(
ccp
(
0
,
0
)
)
;
|
2. 创建两个混合模式对象,分别设置给蒙版图像和被蒙版图像;
这两个 ccBlendFunc 实现了以蒙版图像的像素透明度来显示被蒙版图像。具体的含义,可以参考这里: glBlendFunc。
一个更直观的例子和实时预览工具,可以看这里:Visual glBlendFunc + glBlendEquation Tool 。
1
2
3
4
5
|
ccBlendFunc
__maskBF
=
{
GL_ONE
,
GL
_ONE
}
;
__pMask
->
setBlendFunc
(
__maskBF
)
;
ccBlendFunc
__imgBF
=
{
GL_SRC_ALPHA
,
GL_ONE_MINUS_SRC
_ALPHA
}
;
__pImg
->
setBlendFunc
(
__imgBF
)
;
|
3. 创建一个 CCRenderTexture 的实例,将其初始化为被遮罩图片的大小;
1
2
|
CCSize
__size
=
__pImg
->
getContentSize
(
)
;
CCRenderTexture
*
__pRender
=
CCRenderTexture
::
create
(
__size
.
width
,
__size
.
height
)
;
|
4. 依次绘制蒙版图像和被蒙版图像,注意是先绘制蒙版图像,再绘制被蒙版图像;
1
2
3
4
|
__pRender
->
begin
(
)
;
__pMask
->
visit
(
)
;
__pImg
->
visit
(
)
;
__pRender
->
end
(
)
;
|
5. 创建一个新的 CCTexture2D 对象,并根据此纹理建立一个 CCSprite ,搞定。
1
2
3
4
5
|
CCTexture2D
*
__pTex
=
new
CCTexture2D
(
)
;
__pTex
->
initWithImage
(
__pRender
->
newCCImage
(
true
)
)
;
__pTex
->
autorelease
(
)
;
CCSprite
*
__newSprite
=
CCSprite
::
createWithTexture
(
__pTex
)
;
this
->
addChild
(
__newSprite
)
;
|
在这一步中,还有一个做法,就是直接从 CCRenderTexture 中包含的 CCSprite 对象中获取 CCTexture2D 对象,然后创建新的 CCSprite:
1
2
3
4
|
CCTexture2D
*
__pTex
=
__pRender
->
getSprite
(
)
->
getTexture
(
)
;
CCSprite
*
__newSprite
=
CCSprite
::
createWithTexture
(
__pTex
)
;
__newSprite
->
filpY
(
true
)
;
this
->
addChild
(
__newSprite
)
;
|
但由于得到的纹理是根据Y轴反转过的,我们必须再把它反转回来。
那么上面使用 newCCImage
得到的纹理为什么不用翻转呢?
其实不然, newCCImage(true)
这个调用中的参数 true 就代表需要翻转纹理。
完整代码(C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
ccBlendFunc
__maskBF
=
{
GL_ONE
,
GL
_ONE
}
;
ccBlendFunc
__imgBF
=
{
GL_SRC_ALPHA
,
GL_ONE_MINUS_SRC
_ALPHA
}
;
CCSprite
*
__pMask
=
CCSprite
::
createWithTexture
(
$
sprite
->
getTexture
(
)
)
;
__pMask
->
setAnchorPoint
(
ccp
(
0
,
0
)
)
;
__pMask
->
setPosition
(
ccp
(
0
,
0
)
)
;
CCSprite
*
__pImg
=
CCSprite
::
createWithTexture
(
$
sprite
->
getTexture
(
)
)
;
__pImg
->
setAnchorPoint
(
ccp
(
0
,
0
)
)
;
__pImg
->
setPosition
(
ccp
(
0
,
0
)
)
;
__pMask
->
setBlendFunc
(
__maskBF
)
;
__pImg
->
setBlendFunc
(
__imgBF
)
;
CCSize
__size
=
__pImg
->
getContentSize
(
)
;
CCRenderTexture
*
__pRender
=
CCRenderTexture
::
create
(
__size
.
width
,
__size
.
height
)
;
__pRender
->
begin
(
)
;
__pMask
->
visit
(
)
;
__pImg
->
visit
(
)
;
__pRender
->
end
(
)
;
CCTexture2D
*
__pTex
=
new
CCTexture2D
(
)
;
__pTex
->
initWithImage
(
__pRender
->
newCCImage
(
true
)
)
;
__pTex
->
autorelease
(
)
;
CCSprite
*
__newSprite
=
CCSprite
::
createWithTexture
(
__pTex
)
;
this
->
addChild
(
__newSprite
)
;
|
完整代码(lua)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
--- Create a masked sprite
-- @author zrong(zengrong.net)
-- Creation: 2014-01-21
function
display
.
newMaskedSprite
(
__mask
,
__pic
)
local
__mb
=
ccBlendFunc
:
new
(
)
__mb
.
src
=
GL_ONE
__mb
.
dst
=
GL_ZERO
local
__pb
=
ccBlendFunc
:
new
(
)
__pb
.
src
=
GL_DST_ALPHA
__pb
.
dst
=
GL_ZERO
local
__maskSprite
=
display
.
newSprite
(
__mask
)
:
align
(
display
.
LEFT_BOTTOM
,
0
,
0
)
__maskSprite
:
setBlendFunc
(
__mb
)
local
__picSprite
=
display
.
newSprite
(
__pic
)
:
align
(
display
.
LEFT_BOTTOM
,
0
,
0
)
__picSprite
:
setBlendFunc
(
__pb
)
local
__maskSize
=
__maskSprite
:
getContentSize
(
)
local
__canva
=
CCRenderTexture
:
create
(
__maskSize
.
width
,
__maskSize
.
height
)
__canva
:
begin
(
)
__maskSprite
:
visit
(
)
__picSprite
:
visit
(
)
__canva
:
endToLua
(
)
local
__resultSprite
=
CCSpriteExtend
.
extend
(
CCSprite
:
createWithTexture
(
__canva
:
getSprite
(
)
:
getTexture
(
)
)
)
:
flipY
(
true
)
return
__resultSprite
end
|
性能忧虑
在进行了上面的处理之后,CCSprite 其实就变成了一张单独的纹理图片,在显示的时候就和我们从文件中调用的图片一样进行渲染。
这种做法虽然方便,但它会对GPU造成一定的消耗,毕竟合并的工作是实时完成的。GPU在程序运行的过程中帮我们进行了类似 Photoshop 所做的图层合并处理。如果这样的操作过多,肯定会对性能有影响。
在 cocos2d-x 中实现蒙版支持(二)——使用 CCClippingNode(待完成) 一文中,我们会再看看另一种性能更好的实现遮罩的方法。