Cocos2dx3.x绘制自己的node
发表于2018-06-26
3.x最大的变化就是绘制,通过绘制命令添加到渲染队列中,优化相邻两个渲染命中,如果使用的是同一张纹理,则会并到一个批次中绘制出来。下面就和大家介绍下如何在3.x中绘制自己的node。
下面的例子还顺带开了vbo,vao的话opengles2.0还不支持,所以都被我注释了
h
#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
#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
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
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)才正确。