Cocos2dx的绘制与特效(一) 矩形遮罩
更新日期:
这篇文章可能比较长,我写这篇博客的目的是做一个记录,方便以后自己查阅。
所以逻辑性可能较差,如果你无意间看到了我的博文并且有什么疑问或者建议,请联我
linyn0612@126.com
cocos2dx 版本为3.x,具体为3.3+
为外部提供的接口:
static RectShade* create(Rect rect,Color4B color,float lenght);
rect 中间显示的区域,Rect
color 遮罩的颜色值,Color4B
length 遮罩的渐变区域宽度,float(命名有问题..懒得改了)
完成之后大致的效果如下,一般可以用来做高亮,新手引导,迷雾等功能。
在Cocos以上效果常规的实现方法是首先定义一个继承LayerColor的类,重写其绘制,使用自定义Shader进行绘制,这也是我认为比较好的方法。不过我为了熟悉OpenGL以及Cocos2dx的绘制系统,选择了一个扩展性较强的实现方式。
3.x的渲染机制
cocos2dx中所有的节点(Node)在内存中以树的形式存在,并且以中序进行遍历(左-中-右)。这一点在2.x到3.x的过程中没有改变,改变的3.x是将绘制的部分单独抽出来并封装成为对像。3.x以前是一边遍历一边进行绘制,3.x以后遍历树的时的任务是生成绘制命令,并排序存放起来。最后统一绘制。
也就是说在2.x中重写一个节点的绘制一般只需要override节点的draw函数就可以了。在3.x中则需要再引入一个绘制命令对象。引擎提供了Customcommand这个自定义绘制的类,我们可以直接使用或者派生出来自己的类,怎么做取决于自己。
一般可以这样做:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// RectShadeNode.h
class RectShadeNode : public Node {
public:
...
void onDraw(const Mat4 &transform, uint32_t flags);
virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
...
protect:
...
V2F_C4B_T2F* _buffer;
GLuint _vbo;
CustomCommand _customCommand;
}
1 | // RectShadeNode.cpp |
绘制的数据使用cocos2dx提供的V2F_C4B_T2F结构体,当然这个效果不需要Textrood,完全可以自己定义一个定义只有Vertics与color的结构体,我比较懒就直接用了现成的。
绘制遮罩
具体过程是找到遮罩的8个顶点,之后使用8个顶点建立索引,绘制8个三角形。然后就完成了。
绘图版的笔坏了,还没有鼠标,就原谅我用触摸板画的图吧=。=||
如以上图,我们要绘制由a-h八个顶点组成的8个三角形
在初始化(一般init函数中)对绘制的数据进行赋值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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54bool RectShadeNode::init(Rect rect,Color4B color,float length)
{
//glprogram & vbo & attribs & uniforms
...
drawReckNode(rect,color,length);
return true;
}
void RectShadeNode::drawReckNode(Rect rect,Color4B color,float length)
{
auto size = Director::getInstance()->getWinSize();
unsigned int vertex_count = 8*3;
ensureCapacity(vertex_count);
_positions = (Vec2*)malloc(4*sizeof(Vec2*));
Vec2 position1 = rect.origin;
Vec2 position2 = Vec2(rect.origin.x,rect.origin.y+rect.size.height);
Vec2 position3 = Vec2(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height);
Vec2 position4 = Vec2(rect.origin.x+rect.size.width,rect.origin.y);
_positions[0] = position1;
_positions[1] = position2;
_positions[2] = position3;
_positions[3] = position4;
V2F_C4B_T2F a = {Vec2(0,0), Color4B(color), Tex2F(0.0, 0.0) };
V2F_C4B_T2F b = {Vec2(size.width,0), Color4B(color), Tex2F(0.0, 0.0) };
V2F_C4B_T2F c = {position1, Color4B(color), Tex2F( 0.0, 0.0) };
V2F_C4B_T2F d = {position4, Color4B(color), Tex2F( 0.0, 0.0) };
V2F_C4B_T2F e = {Vec2(size.width,size.height), Color4B(color), Tex2F(0.0, 0.0) };
V2F_C4B_T2F f = {position3, Color4B(color), Tex2F(0.0, 0.0) };
V2F_C4B_T2F g = {position2, Color4B(color), Tex2F( 0.0, 0.0) };
V2F_C4B_T2F h = {Vec2(0,size.height), Color4B(color), Tex2F( 0.0, 0.0) };
V2F_C4B_T2F_Triangle *triangles = (V2F_C4B_T2F_Triangle *)(_buffer + _bufferCount);
V2F_C4B_T2F_Triangle triangle0 = {a, b, c};
V2F_C4B_T2F_Triangle triangle1 = {b, d, c};
V2F_C4B_T2F_Triangle triangle2 = {e, d, b};
V2F_C4B_T2F_Triangle triangle3 = {e, f, d};
V2F_C4B_T2F_Triangle triangle4 = {e, g, f};
V2F_C4B_T2F_Triangle triangle5 = {e, h, g};
V2F_C4B_T2F_Triangle triangle6 = {h, c, g};
V2F_C4B_T2F_Triangle triangle7 = {h, a, c};
triangles[0] = triangle0;
triangles[1] = triangle1;
triangles[2] = triangle2;
triangles[3] = triangle3;
triangles[4] = triangle4;
triangles[5] = triangle5;
triangles[6] = triangle6;
triangles[7] = triangle7;
}
初步的任务已尽完成了
使用自定义Shader达到渐变的效果
效果实现需要传入一个渐变宽度作为参数,令绘制点到高亮的矩形4个边中距离最短的距离为dis。
如果dis < length,则改变其透明度,并且dis在[0,length]中,对应透明度w[color.w,0],且两者成线性。
只需要在fragment shader中判断,并给出w即可。
1 |
|
为了方便计算与判断,我将坐标信息做了初步的处理,还用到了一个内置函数smoothstep函数,这里不赘述其用法。
最后为了实现一个呼吸的效果,只需length*cos(time)就可以了。
1 | float transf(float r) |
整个效果就完成了。
Github:https://github.com/JackLN/ShaderDemo.git
这个项目可能比较乱,什么都有。。。
PS
虽说做出了效果,但也仅仅是为了做出效果…rect,color,length等属性都应该留出更改的接口。
更可以在此基础上扩展出多边形的遮罩,多个不同区域同时高亮等效果。
整个过程是我学习与探索cocos2dx与opengl绘制的过程,最后也完成了一个初步的效果。