cocos2dx-3.x绘制自己的node

3.x最大的变化就是绘制,通过绘制命令添加到渲染队列中,优化相邻两个渲染命中,如果使用的是同一张纹理,则会并到一个批次中绘制出来,具体流程可以看看之前的文章,cocos动画系统及绘制


下面的例子还顺带开了vbo,vao的话opengles2.0还不支持,所以都被我注释了

h

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
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

using namespace cocos2d;

class HelloWorld : public cocos2d::Layer
{
public:
virtual ~HelloWorld();
static cocos2d::Scene* createScene();
virtual bool init() override;
//重写这个方法很重要,node节点递归visit完后就会调这个draw函数,所以我们在这里添加绘制命令绑定自己的绘制函数OnDraw
virtual void draw(Renderer *renderer, const Mat4 &transform, bool transformUpdated) override;
//自己的绘制函数
void onDraw();
void menuCloseCallback4(cocos2d::Ref* pSender);
CREATE_FUNC(HelloWorld);

private:
CustomCommand _customCommand;

//GLuint vao;
GLuint vertexVBO; //缓冲区对象句柄 - 顶点
GLuint indexVBO; //缓冲区对象句柄 - 顶点索引
//当然,你可以可以Gluint vbo[2]用数组去装
};

#endif // __HELLOWORLD_SCENE_H__

cpp

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#include "HelloWorldScene.h"
#include "MySprite.h"
#include "CubeTexture.h"

USING_NS_CC;


Scene* HelloWorld::createScene()
{
// \'scene\' is an autorelease object
auto scene = Scene::create();

Size size = Director::getInstance()->getWinSize();

auto layer2 = Layer::create();
Sprite* sp = Sprite::create("HelloWorld.png");
//MoveTo* move = MoveTo::create(5.0f, Vec2(400, 400));
//sp->runAction(move);

sp->setPosition(size.width / 2, size.height / 2);
//sp->setScale(2.0f);

FontDefinition fd;
fd._fontSize = 40;
FontShadow fs;
fs._shadowEnabled = true;
fs._shadowBlur = 0.1f;
fs._shadowOffset = Size(10.0f, 20.0f);
fs._shadowOpacity = 1.0f;
fd._shadow = fs;
LabelTTF* lb = LabelTTF::createWithFontDefinition("Hello world", fd);
lb->setPosition(size.width / 3, size.height / 3);
layer2->addChild(sp);
layer2->addChild(lb);
//scene->addChild(layer2);

// \'layer\' is an autorelease object
auto layer = HelloWorld::create();

// add layer as a child to scene
scene->addChild(layer);

// return the scene
return scene;
}


void HelloWorld::menuCloseCallback4(cocos2d::Ref* pSender)
{
auto scene = CubeTexture::createScene();
Director::getInstance()->replaceScene(scene);
}


// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}

auto closeItem4 = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback4, this));
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
closeItem4->setPosition(Vec2(origin.x + visibleSize.width / 2 - closeItem4->getContentSize().width / 2,
origin.y + visibleSize.height / 2 + closeItem4->getContentSize().height / 2));

// create menu, it\'s an autorelease object
auto menu = Menu::create(closeItem4, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 100);

//create my own program
auto glprogram = new GLProgram;
glprogram->initWithFilenames("myVertextShader.vert", "myFragmentShader.frag");
glprogram->link();
//set uniform locations
glprogram->updateUniforms();
setGLProgram(glprogram); //设置为自己的shader

//glGenVertexArrays(1, &vao);
//glBindVertexArray(vao);


glGenBuffers(1, &vertexVBO); //创建缓冲区句柄
glGenBuffers(1, &indexVBO);

return true;
}

void HelloWorld::draw(cocos2d::Renderer *renderer, const Mat4 &transform, bool transformUpdated)
{
_customCommand.init(_globalZOrder); //绘制优先级
_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);//绑定自定义绘制函数,由于是用自己定于的顶点数据,所以没用矩阵信息
renderer->addCommand(&_customCommand); //把渲染命令丢进绘制队列
}

void HelloWorld::onDraw()
{
//如果使用对等矩阵,则三角形绘制会在最前面
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);


Mat4 modelViewMatrix;
Mat4::createLookAt(Vec3(0,0,1), Vec3(0,0,0), Vec3(0,1,0), &modelViewMatrix);
modelViewMatrix.translate(2, 2, -5);

//static float move = 0;
//modelViewMatrix.translate(sinf(move*0.05)*2, 2, -5);
//move++;

//static float rotation = 0;
//modelViewMatrix.rotate(Vec3(1,1,1),CC_DEGREES_TO_RADIANS(rotation));
//rotation++;
//if (rotation > 360) {
// rotation = 0;
//}

Mat4 projectionMatrix;
Mat4::createPerspective(60, 480/320, 1.0, 42, &projectionMatrix);

Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, projectionMatrix);
Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, modelViewMatrix);

auto glProgram = getGLProgram();
glProgram->use();
glProgram->setUniformsForBuiltins();

Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

typedef struct {
float Position[3];
float Color[4];
} Vertex;

// auto size = Director::getInstance()->getVisibleSize();
Vertex data[] =
{
// Front
{ { 1, -1, 0 }, { 1, 0, 0, 1 } },
{ { 1, 1, 0 }, { 0, 1, 0, 1 } },
{ { -1, 1, 0 }, { 0, 0, 1, 1 } },
{ { -1, -1, 0 }, { 0, 0, 0, 1 } },
// Back
{ { 1, 1, -2 }, { 1, 0, 0, 1 } },
{ { -1, -1, -2 }, { 0, 1, 0, 1 } },
{ { 1, -1, -2 }, { 0, 0, 1, 1 } },
{ { -1, 1, -2 }, { 0, 0, 0, 1 } },
// Left
{ { -1, -1, 0 }, { 1, 0, 0, 1 } },
{ { -1, 1, 0 }, { 0, 1, 0, 1 } },
{ { -1, 1, -2 }, { 0, 0, 1, 1 } },
{ { -1, -1, -2 }, { 0, 0, 0, 1 } },
// Right
{ { 1, -1, -2 }, { 1, 0, 0, 1 } },
{ { 1, 1, -2 }, { 0, 1, 0, 1 } },
{ { 1, 1, 0 }, { 0, 0, 1, 1 } },
{ { 1, -1, 0 }, { 0, 0, 0, 1 } },
// Top
{ { 1, 1, 0 }, { 1, 0, 0, 1 } },
{ { 1, 1, -2 }, { 0, 1, 0, 1 } },
{ { -1, 1, -2 }, { 0, 0, 1, 1 } },
{ { -1, 1, 0 }, { 0, 0, 0, 1 } },
// Bottom
{ { 1, -1, -2 }, { 1, 0, 0, 1 } },
{ { 1, -1, 0 }, { 0, 1, 0, 1 } },
{ { -1, -1, 0 }, { 0, 0, 1, 1 } },
{ { -1, -1, -2 }, { 0, 0, 0, 1 } }
};

GLubyte indices[] = {
// Front
0, 1, 2,
2, 3, 0,
// Back
4, 5, 6,
4, 5, 7,
// Left
8, 9, 10,
10, 11, 8,
// Right
12, 13, 14,
14, 15, 12,
// Top
16, 17, 18,
18, 19, 16,
// Bottom
20, 21, 22,
22, 23, 20
};

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); //绑定这个缓冲区句柄为顶点类型
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); //申请内存大小和绑定数据,静态绘制,有几种枚举类型这里就不说了,自行百度

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO);//绑定这个缓冲区句柄为索引类型
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

GLuint positionLocation = glGetAttribLocation(glProgram->getProgram(), "a_position"); //获取自定义shader中的属性变量的地址
GLuint colorLocation = glGetAttribLocation(glProgram->getProgram(), "a_color");

glEnableVertexAttribArray(positionLocation);//激活
glEnableVertexAttribArray(colorLocation);

glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Position));
glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Color));



// 0 ~ 1 之间波动
static float colorCount = 0;
float tmp = sinf(colorCount*0.01);
tmp = tmp < 0 ? -tmp : tmp;
colorCount++;

/* ---------------------- test1, 为一个float赋值 */
//GLuint uColorLocation1 = glGetUniformLocation(glProgram->getProgram(), "specIntensity");
//float specIntensity = tmp;
//glUniform1f(uColorLocation1, specIntensity);

/* ---------------------- test2, 为1个由4个float组成的vec4赋值 */
//GLuint uColorLocation2 = glGetUniformLocation(glProgram->getProgram(), "u_color");
//float uColor2[] = { tmp, tmp, tmp, tmp };
//glUniform4fv(uColorLocation2, 1, uColor2);

/* ---------------------- test3, 为float[]数组中的两个元素赋值 */
//GLuint uColorLocation3 = glGetUniformLocation(glProgram->getProgram(), "threshold");
//float threshold[2] = { 0.5, tmp };
//glUniform1fv(uColorLocation3, 2, threshold);

/* ---------------------- test4, 为vec4[]数组中的3个元素赋值 */
//GLuint uColorLocation4 = glGetUniformLocation(glProgram->getProgram(), "colors");
//float colors[12] = { 0.4, 0.4, 0.8, 1.0,
// 0.2, 0.2, 0.4, 1.0,
// 1.0, 1.0, 1.0, tmp };
//glUniform4fv(uColorLocation4, 3, colors);

/* ---------------------- test5, 为float[]数组中的某个元素赋值,根据下标索引 */
GLuint uColorLocation5 = glGetUniformLocation(glProgram->getProgram(), "threshold[0]"); //传入自定shader中的uniform变量,控制alpha透明度
float asd = tmp;
glUniform1f(uColorLocation5, asd);

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE,(GLvoid*)0); //绘制三角形,由于索引也上传到缓冲区了,所以这里的索引直接传0地址

//解除激活的缓冲区
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 36);
CHECK_GL_ERROR_DEBUG();
}

HelloWorld::~HelloWorld()
{
//清除缓冲区句柄
glDeleteBuffers(1, &vertexVBO);
glDeleteBuffers(1, &indexVBO);
}

myVertextShader.vert

1
2
3
4
5
6
7
8
9
10
11
12
attribute vec3 a_position;
attribute vec4 a_color;

varying vec4 v_fragmentColor;

varying vec3 normal;

void main()
{
gl_Position = CC_MVPMatrix * vec4(a_position.xyz,1.0);
v_fragmentColor = a_color;
}

myFragmentShader.frag

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
varying vec4 v_fragmentColor;

uniform float specIntensity;
uniform vec4 u_color;
uniform float threshold[2];
uniform vec4 colors[3];

vec4 toonify(float intensity)
{
vec4 color;
if (intensity < 0.3)
color = vec4(1.0, 0.0 , 0.0, 0.5);
else if (intensity < 0.5)
color = vec4(0.0, 1.0 , 0.0, 0.5);
else if (intensity < 0.7)
color = vec4(0.0, 0.0 , 1.0, 0.5);
else
{
color = vec4(1.0, 1.0, 1.0, 0.5);
//discard; //最后的discard关键字只能在片断shader中使用,它将在不写入帧缓存或者深度缓存的情况下,终止当前片断的shader程序。
}

return(color);
}

void main()
{
// ---------------------- test1
// gl_FragColor = v_fragmentColor * vec4(1.0, 1.0, 1.0, specIntensity);

// ---------------------- test2
//gl_FragColor = v_fragmentColor * u_color;

// ---------------------- test3
//gl_FragColor = v_fragmentColor * vec4(1.0, 1.0, 1.0, threshold[1]);

// ---------------------- test4
//gl_FragColor = v_fragmentColor * colors[2];

// ---------------------- test5
gl_FragColor = v_fragmentColor * vec4(1.0, 1.0, 1.0, threshold[0]);

// ---------------------- test6
//gl_FragColor = toonify(threshold[0]);
}

注释中的其他uniform可以解开注释,同时shader也解开


shader编写需要注意的是

如果shader中定义了uniform属性名但没有使用,会在编译shader时优化调,所以在c++中获取uniform中的变量地址会是个空地址
其次就是类型需要严格一直,在windows平台体现不出来,应为win平台用的是opengl4.0的,而手机平台的是opengles2.0标准,类型不一致在运行时导致异常崩溃,崩溃信息可以重eclipse中看到,比如 float a = 4.0 * 123就是错的,float*int,应改为 float a = 4.0 * float(123)才正确