1开始,先创建一个LayerColor
Scene *scene=Scene::create(); director->runWithScene(scene); //目标 auto layer = LayerColor::create(Color4B(0,255,0,255),100,100); //主要的步骤就是设置了node 的 _position layer->setPosition(10,10); scene->addChild(layer);
2 看一下LayerColor的初始化方法
bool LayerColor::initWithColor(const Color4B& color,GLfloat w,GLfloat h) { if (Layer::init()) { // default blend function //指定混合模式 _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED; /* realColor和displayedColor 记录元素本身的颜色属性 displayedColor和displayedOpacity方法用于表示和父亲元素叠加过后的最终绘制颜色 sprite用于父亲颜色和自己纹理的混合,LayColor默认一致,不叠加颜色 */ _displayedColor.r = _realColor.r = color.r; _displayedColor.g = _realColor.g = color.g; _displayedColor.b = _realColor.b = color.b; _displayedOpacity = _realOpacity = color.a; //四个顶点 初始化 for (size_t i = 0; i<sizeof(_squareVertices) / sizeof( _squareVertices[0]); i++ ) { _squareVertices[i].x = 0.0f; _squareVertices[i].y = 0.0f; } //四个顶点的颜色归一化,颜色是一样的 updateColor(); //w,h 为 设计分辨率,设置顶点的范围 setContentSize(Size(w,h)); /* 每个node拥有一个GLProgramState实例 查找这种类型的shader GLProgram SHADER_NAME_POSITION_COLOR_NO_MVP */ GLProgramState* state=GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP); setGLProgramState(state); return true; } return false; }
/// override contentSize void LayerColor::setContentSize(const Size & size) { //没有赋值的为0,也就是 0(0,0) 1(w,0) 2 (0,h) 3(w,h) //绘制顺序为012 213 _squareVertices[1].x = size.width; _squareVertices[2].y = size.height; _squareVertices[3].x = size.width; _squareVertices[3].y = size.height; Layer::setContentSize(size); } void Node::setContentSize(const Size & size) { if ( ! size.equals(_contentSize)) { _contentSize = size; //得到锚点在本地坐标系下的坐标 _anchorPointInPoints = Vec2(_contentSize.width * _anchorPoint.x,_contentSize.height * _anchorPoint.y ); //告诉该更新了 _transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true; } }
3 Director::drawScene方法为正式绘图,如下
void Director::drawScene() { //省略部分代码 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //兼容cocos2.0,暂时忽略 pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // draw the scene if (_runningScene) {//正式访问 _runningScene->visit(_renderer,Mat4::IDENTITY,false);//第一次的矩阵是单位矩阵 _eventDispatcher->dispatchEvent(_eventAfterVisit); } //开始真正的opengl _renderer->render(); popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); _totalFrames++; // swap buffers if (_openGLView) { _openGLView->swapBuffers();//这个之后再看 } if (_displayStats) { calculateMPF();//不知道啥意思 } }
标红不分为开始遍历节点,但不进行opengl绘制,进入代码如下:
void Node::visit(Renderer* renderer,const Mat4 &parentTransform,uint32_t parentFlags) { // quick return if not visible. children won‘t be drawn. if (!_visible) { return; } uint32_t flags = processParentFlags(parentTransform,parentFlags); // IMPORTANT: // To ease the migration to v3.0,we still support the Mat4 stack,// but it is deprecated and your code should not rely on it /* 为了便于迁移到v3.0,我们仍然支持Mat4堆栈, 但它已被弃用,您的代码不应该依赖它 */ Director* director = Director::getInstance(); director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); //把刚生产的模型视图矩阵加入到顶部的modeview【对scene来说,执行下面代码之前,里面已经有3个modelview】 //这个代码针对2.0的,对3.1没啥意义了 director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW,_modelViewTransform); int i = 0; if(!_children.empty()) { sortAllChildren(); // draw children zOrder < 0 for( ; i < _children.size(); i++ ) { auto node = _children.at(i); if ( node && node->_localZOrder < 0 ) node->visit(renderer,_modelViewTransform,flags); else break; } // self draw this->draw(renderer,flags); for(auto it=_children.cbegin()+i; it != _children.cend(); ++it) (*it)->visit(renderer,flags); } else { this->draw(renderer,flags); } /* 画完了就退出战,是兼容2.0的时候,现在不需要这个了 矩阵的转换都在GPU的shader中了 */ director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // FIX ME: Why need to set _orderOfArrival to 0?? // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920 // reset for next frame // _orderOfArrival = 0; }
uint32_t Node::processParentFlags(const Mat4& parentTransform,uint32_t parentFlags) { uint32_t flags = parentFlags;//先把父亲的改动标志传过来 //如果转置矩阵 变化了, 标记为脏数据,要更新 flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0); //如果大小改变了,标记为脏数据,要更新 flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0); // if(_usingNormalizedPosition && (flags & FLAGS_CONTENT_SIZE_DIRTY)) { CCASSERT(_parent,"setNormalizedPosition() doesn‘t work with orphan nodes"); auto s = _parent->getContentSize(); _position.x = _normalizedPosition.x * s.width; _position.y = _normalizedPosition.y * s.height; _transformUpdated = _transformDirty = _inverseDirty = true; } //转置矩阵和大小其中一个变了 if(flags & FLAGS_DIRTY_MASK) _modelViewTransform = this->transform(parentTransform);//节点自己的转换矩阵,节点的本地坐标乘以这个矩阵就会得到世界坐标 //更新完毕,标记为false _transformUpdated = false; _contentSizeDirty = false; return flags; }
self->draw为绘制自己,但不是真的绘制,而是让自己关联一个绘制命令Command,LayerColor的draw方法重写如下:
//transform为本地坐标转世界坐标的矩阵 void LayerColor::draw(Renderer *renderer,const Mat4 &transform,uint32_t flags) { _customCommand.init(_globalZOrder); //回调函数 _customCommand.func = CC_CALLBACK_0(LayerColor::onDraw,this,transform,flags); //把绘制命令的东西放到renderer里面 renderer->addCommand(&_customCommand); for(int i = 0; i < 4; ++i) { Vec4 pos; //四个顶点的设计分辨率坐标 pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ; pos.w = 1;//齐次坐标 //得出来的pos就是世界坐标下的pos了 _modelViewTransform.transformVector(&pos); //pos.w 世界坐标的w始终为1 //这个世界坐标会在shader内乘以相机矩阵和裁剪矩阵,得出最后的视口需要的坐标 _noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w; } }
代码把需要绘制的信息加入到了customCommand里面。
4 _renderer->render(); 负责执行command内的opengl绘制命令,代码如下:
void Renderer::render() { //Process render commands //1. Sort render commands based on ID //renderGroups包括若干rederqueue,默认使用第一个, //renderquque包括若干个rendercommand for (auto &renderqueue : _renderGroups) { renderqueue.sort(); } visitRenderQueue(_renderGroups[0]); flush(); clean(); }
void Renderer::visitRenderQueue(const RenderQueue& queue) { ssize_t size = queue.size(); for (ssize_t index = 0; index < size; ++index) { auto command = queue[index]; auto commandType = command->getType(); if(RenderCommand::Type::CUSTOM_COMMAND == commandType)//比如 LayerColor { flush(); auto cmd = static_cast<CustomCommand*>(command); cmd->execute();//会调用LayerColor::onDraw,直接开始绘图 } } }
5 cmd->execute会调用customCommand的回调函数,在LayerColor中为onDraw,代码如下:
//通过自定义方法进行回调 transform为 本地坐标转世界坐标的旋转矩阵 void LayerColor::onDraw(const Mat4& transform,uint32_t flags) { getGLProgram()->use();//layercolor 的tansform为相机矩阵*裁剪矩阵 getGLProgram()->setUniformsForBuiltins(transform);//设置顶点着色器中全局变量的值,如MVP矩阵 //启用 顶点坐标和颜色 GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR ); // // Attributes // #ifdef EMSCRIPTEN setGLBufferData(_noMVPVertices,4 * sizeof(Vec3),0); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,GL_FLOAT,GL_FALSE,0); setGLBufferData(_squareColors,4 * sizeof(Color4F),1); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,0); #else //找到顶点的索引 _noMVPVertices为世界坐标中的四个顶点的值,存在了cpu中,没有存到显存 glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,0,_noMVPVertices); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,_squareColors); #endif // EMSCRIPTEN //混合,源和目标 颜色的混合 GL::blendFunc( _blendFunc.src,_blendFunc.dst ); //画这四个点 glDrawArrays(GL_TRIANGLE_STRIP,4); //这是记录图元和顶点吗 // CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4); auto __renderer__ = Director::getInstance()->getRenderer(); __renderer__->addDrawnBatches(1); __renderer__->addDrawnVertices(4); }
LayerColor绘制过程比较简单,没有纹理设置,只有顶点和颜色,通过glDrawArrays绘制完成