2dx 3.x学习 cocos_cocos2dx3.17和cocos2dx-3.3 - CSDN
  • 第一,渲染流程从2.x3.x的变化。  在2.x中,渲染过程是通过递归渲染树(Rendering tree)这种图关系来渲染关系图。递归调用visit()函数,并且在visit()函数中调用该节点的draw函数渲染各个节点,此时draw函数的作用...

       




    最近几天,我都在学习如何在Cocos2d-x3.2中使用OpenGL来实现对图形的渲染。在网上也看到了很多好的文章,我在它们的基础上做了这次的我个人认为比较完整的总结。当你了解了Cocos2d-x3.2中对图形渲染的流程,你就会觉得要学会写自己的shader才是最重要的。

        第一,渲染流程从2.x到3.x的变化。

        在2.x中,渲染过程是通过递归渲染树(Rendering tree)这种图关系来渲染关系图。递归调用visit()函数,并且在visit()函数中调用该节点的draw函数渲染各个节点,此时draw函数的作用是直接调用OpenGL代码进行图形的渲染。由于visit()和draw函数都是虚函数,所以要注意执行时的多态。那么我们来看看2.x版本中CCSprite的draw函数,如代码1。

        代码1:

        //这是cocos2d-2.0-x-2.0.4版本的CCSprite的draw函数  
        void CCSprite::draw(void)  
        {  
            CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");  
            CCAssert(!m_pobBatchNode, "If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");  
            CC_NODE_DRAW_SETUP();  
            ccGLBlendFunc( m_sBlendFunc.src, m_sBlendFunc.dst );  
            if (m_pobTexture != NULL)  
            {  
                ccGLBindTexture2D( m_pobTexture->getName() );  
            }  
            else  
            {  
                ccGLBindTexture2D(0);  
            }    
            //  
            // Attributes  
            //  
            ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );  
        #define kQuadSize sizeof(m_sQuad.bl)  
            long offset = (long)&m_sQuad;  
            // vertex  
            int diff = offsetof( ccV3F_C4B_T2F, vertices);  
            glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));  
            // texCoods  
            diff = offsetof( ccV3F_C4B_T2F, texCoords);  
            glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));  
            // color  
            diff = offsetof( ccV3F_C4B_T2F, colors);  
            glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));  
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
            CHECK_GL_ERROR_DEBUG();  
        #if CC_SPRITE_DEBUG_DRAW == 1  
            // draw bounding box  
            CCPoint vertices[4]={  
                ccp(m_sQuad.tl.vertices.x,m_sQuad.tl.vertices.y),  
                ccp(m_sQuad.bl.vertices.x,m_sQuad.bl.vertices.y),  
                ccp(m_sQuad.br.vertices.x,m_sQuad.br.vertices.y),  
                ccp(m_sQuad.tr.vertices.x,m_sQuad.tr.vertices.y),  
            };  
            ccDrawPoly(vertices, 4, true);  
        #elif CC_SPRITE_DEBUG_DRAW == 2  
            // draw texture box  
            CCSize s = this->getTextureRect().size;  
            CCPoint offsetPix = this->getOffsetPosition();  
            CCPoint vertices[4] = {  
                ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y),  
                ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height)  
            };  
            ccDrawPoly(vertices, 4, true);  
        #endif // CC_SPRITE_DEBUG_DRAW  
          
            CC_INCREMENT_GL_DRAWS(1);  
          
            CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");  
        }  



         那么我们也看看3.x中Sprite的draw函数,如代码2。

            代码2:

        void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)  
        {  
            // Don't do calculate the culling if the transform was not updated  
            _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;  
            if(_insideBounds)  
            {  
                _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);  
                renderer->addCommand(&_quadCommand);  
        #if CC_SPRITE_DEBUG_DRAW  
                _customDebugDrawCommand.init(_globalZOrder);  
                _customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);  
                renderer->addCommand(&_customDebugDrawCommand);  
        #endif //CC_SPRITE_DEBUG_DRAW  
            }  
        }  

        从代码1和代码2的对比中,我们很容易就发现2.x版本中的draw函数直接调用OpengGL代码进行图形渲染,而3.x版本中draw的作用是把RenderCommand添加到CommandQueue中,至于这样做的好处是,实际的渲染API进入其中一个与显卡直接交流的有独立线程的RenderQueue。

        从Cocos2d-x3.0开始,Cocos2d-x引入了新的渲染流程,它不像2.x版本直接在每一个node中的draw函数中直接调用OpenGL代码进行图形渲染,而是通过各种RenderCommand封装起来,然后添加到一个CommandQueue队列里面去,而现在draw函数的作用就是在此函数中设置好相对应的RenderCommand参数,然后把此RenderCommand添加到CommandQueue中。最后在每一帧结束时调用renderer函数进行渲染,在renderer函数中会根据ID对RenderCommand进行排序,然后才进行渲染。

       下面我们来看看图1、图2,这两个图形象地表现了Cocos2d-x3.x下RenderCommand的封装与传递与及RenderCommand的排序。

        图1:

         


        图2:

          

         上面所说的各个方面都有点零碎,下面就对渲染的整个流程来一个从头到尾的梳理吧。下面是针对3.2版本的,对于2.x版本的梳理不做梳理,因为我用的是3.2版本。

       首先,我们Cocos2d-x的执行是通过Application::run()来开始的,如代码3,此代码目录中在xx\cocos2d\cocos\platform\对应平台的目录下,这是与多平台实现有关的类,关于如何实现多平台的编译,你可以参考《cocos2d-x3.2源码分析(一)类FileUtils--实现把资源放在Resources文件目录下达到多平台的引用 》中我对平台编译的分析。以防篇幅过长,只截取了重要部分,如需详解,可以直接查看源码。

       代码3:

        int Application::run()  
        {  
          ...  
          director->mainLoop();  
          ...  
         }  


        从代码3中,它明显的启发着我们要继续追寻Director::mainLoop()函数。在Director中mainLoop()为纯函数,此子类DisplayLinkDirector才有其实现,如代码4。

       代码4:

        void DisplayLinkDirector::mainLoop()  
        {     
            <span><span class="comment">//只有一种情况会调用到这里来,就是导演类调用end函数</span><span>  </span></span>  
            if (_purgeDirectorInNextLoop)  
            {  
                _purgeDirectorInNextLoop = false;  
                <span><span class="comment">//清除导演类</span><span></span></span>  
                purgeDirector();  
            }  
            else if (! _invalid)  
            {   <span><span class="comment">//绘制</span><span> </span></span>  
                drawScene();   
                //清除当前内存池中对象,即池中每一个对象--_referenceCount  
                PoolManager::getInstance()->getCurrentPool()->clear();  
            }  
        }  


       mainLoop是主线程调用的循环,其中drawScene()是绘制函数,接着我们继续追寻它的代码,如代码5。

       代码5:

        void Director::drawScene()  
        {  
            <span><span class="comment">//计算间隔时间</span><span> </span></span>  
            calculateDeltaTime();  
              
            //忽略该帧如果时间间隔接近0  
            if(_deltaTime < FLT_EPSILON)  
            {  
                return;  
            }  
          
            if (_openGLView)  
            {  
                _openGLView->pollInputEvents();  
            }  
          
            //tick before glClear: issue #533  
            if (! _paused)  
            {  
                _scheduler->update(_deltaTime);  
                _eventDispatcher->dispatchEvent(_eventAfterUpdate);  
            }  
          
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
          
            /* to avoid flickr, nextScene MUST be here: after tick and before draw. 
             XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */  
            if (_nextScene)  
            {  
                setNextScene();  
            }  
          
            pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
          
            // draw the scene  
            if (_runningScene)  
            {  
                _runningScene->visit(_renderer, Mat4::IDENTITY, false);  
                _eventDispatcher->dispatchEvent(_eventAfterVisit);  
            }  
          
            // draw the notifications node  
            if (_notificationNode)  
            {  
                _notificationNode->visit(_renderer, Mat4::IDENTITY, false);  
            }  
          
            if (_displayStats)  
            {  
                showStats();  
            }  
          
            _renderer->render();  
            _eventDispatcher->dispatchEvent(_eventAfterDraw);  
          
            popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
          
            _totalFrames++;  
          
            // swap buffers  
            if (_openGLView)  
            {  
                _openGLView->swapBuffers();  
            }  
          
            if (_displayStats)  
            {  
                calculateMPF();  
            }  
        }  

          从代码5中,我们看见visit()和render()函数的调用。其中visit()函数会调用draw()函数来向RenderQueue中添加RenderCommand,那么就继续追寻visit()的代码,如代码6。

         代码6: 

        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  
            Director* director = Director::getInstance();  
            director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
            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, _modelViewTransform, flags);  
          
                for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)  
                    (*it)->visit(renderer, _modelViewTransform, flags);  
            }  
            else  
            {  
                this->draw(renderer, _modelViewTransform, flags);  
            }  
          
            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;  
        }  
       从代码6中,我们可以看到“ auto node = _children.at(i);和node->visit(renderer, _modelViewTransform, flags);”,这段代码的意思是先获取子节点,然后递归调用节点的visit()函数,到了没有子节点的节点,开始调用draw()函数。那么我们看看draw()函数代码,如代码7。

       代码7:

        void Node::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags)  
        {  
        }  

       好吧,从代码7中,我们看到Node的draw什么都没有做,是我们找错地方?原来draw()是虚函数,所以它执行时执行的是该字节类的draw()函数。确实是我们找错地方了。那么我们分别看DrawNode::draw()、Sprite::draw()。

       代码8:

        void DrawNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)  
        {  
            _customCommand.init(_globalZOrder);  
            _customCommand.func = CC_CALLBACK_0(DrawNode::onDraw, this, transform, flags);  
            renderer->addCommand(&_customCommand);  
        }  
          
        void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)  
        {  
            // Don't do calculate the culling if the transform was not updated  
            _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;  
          
            if(_insideBounds)  
            {  
                _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);  
                renderer->addCommand(&_quadCommand);  
        #if CC_SPRITE_DEBUG_DRAW  
                _customDebugDrawCommand.init(_globalZOrder);  
                _customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);  
                renderer->addCommand(&_customDebugDrawCommand);  
        #endif //CC_SPRITE_DEBUG_DRAW  
            }  
        }  

         从代码8中,我们可以看到在draw()函数向RenderQueue中添加RenderCommand,当然有的类的draw()不是向RenderQueue中添加RenderCommand,而是直接使用OpenGL的API直接进行渲染,或者做一些其他的事情。

        那么当draw()都递归调用完了,我们来看看最后进行渲染的Renderer::render() 函数,如代码9。

        代码9:

     void Renderer::render()  
        {  
            //Uncomment this once everything is rendered by new renderer  
            //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
          
            //TODO setup camera or MVP  
            _isRendering = true;  
              
            if (_glViewAssigned)  
            {  
                // cleanup  
                _drawnBatches = _drawnVertices = 0;  
          
                //Process render commands  
                //1. Sort render commands based on ID  
                for (auto &renderqueue : _renderGroups)  
                {  
                    renderqueue.sort();  
                }  
                visitRenderQueue(_renderGroups[0]);  
                flush();  
            }  
            clean();  
            _isRendering = false;  
        }  


       从代码9中,我们看到“renderqueue.sort()",这是之前所说的对命令先排序,然后才进行渲染,“visitRenderQueue( _renderGroups[0])”就是来进行渲染的。那么我们接着看看void Renderer::visitRenderQueue(const RenderQueue& queue)的代码,如代码10。

       代码10:

     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::QUAD_COMMAND == commandType)  
                {  
                    flush3D();  
                    auto cmd = static_cast<QuadCommand*>(command);  
                    //Batch quads  
                    if(_numQuads + cmd->getQuadCount() > VBO_SIZE)  
                    {  
                        CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() < VBO_SIZE, "VBO is not big enough for quad data, please break the quad data down or use customized render command");  
                          
                        //Draw batched quads if VBO is full  
                        drawBatchedQuads();  
                    }  
                      
                    _batchedQuadCommands.push_back(cmd);  
                      
                    memcpy(_quads + _numQuads, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());  
                    convertToWorldCoordinates(_quads + _numQuads, cmd->getQuadCount(), cmd->getModelView());  
                      
                    _numQuads += cmd->getQuadCount();  
          
                }  
                else if(RenderCommand::Type::GROUP_COMMAND == commandType)  
                {  
                    flush();  
                    int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();  
                    visitRenderQueue(_renderGroups[renderQueueID]);  
                }  
                else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)  
                {  
                    flush();  
                    auto cmd = static_cast<CustomCommand*>(command);  
                    cmd->execute();  
                }  
                else if(RenderCommand::Type::BATCH_COMMAND == commandType)  
                {  
                    flush();  
                    auto cmd = static_cast<BatchCommand*>(command);  
                    cmd->execute();  
                }  
                else if (RenderCommand::Type::MESH_COMMAND == commandType)  
                {  
                    flush2D();  
                    auto cmd = static_cast<MeshCommand*>(command);  
                    if (_lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID())  
                    {  
                        flush3D();  
                        cmd->preBatchDraw();  
                        cmd->batchDraw();  
                        _lastBatchedMeshCommand = cmd;  
                    }  
                    else  
                    {  
                        cmd->batchDraw();  
                    }  
                }  
                else  
                {  
                    CCLOGERROR("Unknown commands in renderQueue");  
                }  
            }  
        }
        从代码10中,我们看到RenderCommand类型有QUAD_COMMAND,CUSTOM_COMMAND,BATCH_COMMAND,

    GROUP_COMMAND,MESH_COMMAND五种,这些类型的讲解在下一节。

       从代码10中,好像没有与OpenGL相关的代码,有点囧。其实这OpenGL的API调用是在Renderer::drawBatched

    Quads()、BatchCommand::execute()中。在代码10中,我们也看到在QUAD_COMMAND类型中调用了drawBatchedQuads(),如代码11。在CUSTOM_COMMAND中调用了CustomCommand::execute(),如代码12。在BATCH_COMMAND中调用了BatchCommand::execute(),如代码13。在MESH_COMMAND类型中调用了MeshCommand::preBatchDraw()和MeshCommand::batchDraw()。至于GROUP_COMMAND类型,就递归它组里的成员。

        代码11:

        void Renderer::drawBatchedQuads()  
        {  
            //TODO we can improve the draw performance by insert material switching command before hand.  
          
            int quadsToDraw = 0;  
            int startQuad = 0;  
          
            //Upload buffer to VBO  
            if(_numQuads <= 0 || _batchedQuadCommands.empty())  
            {  
                return;  
            }  
          
            if (Configuration::getInstance()->supportsShareableVAO())  
            {  
                //Set VBO data  
                glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
          
                // option 1: subdata  
        //        glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] );  
          
                // option 2: data  
        //        glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * (n-start), &quads_[start], GL_DYNAMIC_DRAW);  
          
                // option 3: orphaning + glMapBuffer  
                glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * (_numQuads), nullptr, GL_DYNAMIC_DRAW);  
                void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);  
                memcpy(buf, _quads, sizeof(_quads[0])* (_numQuads));  
                glUnmapBuffer(GL_ARRAY_BUFFER);  
          
                glBindBuffer(GL_ARRAY_BUFFER, 0);  
          
                //Bind VAO  
                GL::bindVAO(_quadVAO);  
            }  
            else  
            {  
        #define kQuadSize sizeof(_quads[0].bl)  
                glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
          
                glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _numQuads , _quads, GL_DYNAMIC_DRAW);  
          
                GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);  
          
                // vertices  
                glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices));  
          
                // colors  
                glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors));  
          
                // tex coords  
                glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords));  
          
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);  
            }  
          
            //Start drawing verties in batch  
            for(const auto& cmd : _batchedQuadCommands)  
            {  
                auto newMaterialID = cmd->getMaterialID();  
                if(_lastMaterialID != newMaterialID || newMaterialID == QuadCommand::MATERIAL_ID_DO_NOT_BATCH)  
                {  
                    //Draw quads  
                    if(quadsToDraw > 0)  
                    {  
                        glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );  
                        _drawnBatches++;  
                        _drawnVertices += quadsToDraw*6;  
          
                        startQuad += quadsToDraw;  
                        quadsToDraw = 0;  
                    }  
          
                    //Use new material  
                    cmd->useMaterial();  
                    _lastMaterialID = newMaterialID;  
                }  
          
                quadsToDraw += cmd->getQuadCount();  
            }  
          
            //Draw any remaining quad  
            if(quadsToDraw > 0)  
            {  
                glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );  
                _drawnBatches++;  
                _drawnVertices += quadsToDraw*6;  
            }  
          
            if (Configuration::getInstance()->supportsShareableVAO())  
            {  
                //Unbind VAO  
                GL::bindVAO(0);  
            }  
            else  
            {  
                glBindBuffer(GL_ARRAY_BUFFER, 0);  
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
            }  
            _batchedQuadCommands.clear();  
            _numQuads = 0;  

        代码12:

     void CustomCommand::execute()  
        {  
            if(func)  
            {  
                func();  
            }  
        }  

        代码13:  

        void BatchCommand::execute()  
        {  
            // Set material  
            _shader->use();  
            _shader->setUniformsForBuiltins(_mv);  
            GL::bindTexture2D(_textureID);  
            GL::blendFunc(_blendType.src, _blendType.dst);  
          
            // Draw  
            _textureAtlas->drawQuads();  
        }  

       从代码11、代码12、代码13中,我们都看到了这些函数中对OpenGl的API调用来进行渲染。其中特别提醒一下,在CustomCommand::execute()中直接调用的函数是我们设置的回调函数。在这个函数中,我们可以自己使用OpenGL的API进行图形的渲染。这就在第三节中讲如何在Cocos2d-x中自己设置渲染功能中向_customCommand添加的函数。在这里我先给出简便的方式,_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this)。

       以上就是把一个完整的渲染的流程都梳理了一片,下面我给出了流程图,如图3。

       图3:

      



       第二,RenderCommand的类型。

        这里的类型讲解主要参考这篇文章中关于RenderComman的类型讲解。

        QUAD_COMMAND:QuadCommand类绘制精灵等。
       所有绘制图片的命令都会调用到这里,处理这个类型命令的代码就是绘制贴图的openGL代码,下一篇文章会详细介绍这部分代码。
      CUSTOM_COMMAND:CustomCommand类自定义绘制,自己定义绘制函数,在调用绘制时只需调用已经传进来的回调函数就可以,裁剪节点,绘制图形节点都采用这个绘制,把绘制函数定义在自己的类里。这种类型的绘制命令不会在处理命令的时候调用任何一句openGL代码,而是调用你写好并设置给func的绘制函数,后续文章会介绍引擎中的所有自定义绘制,并自己实现一个自定义的绘制。
      BATCH_COMMAND:BatchCommand类批处理绘制,批处理精灵和粒子
      其实它类似于自定义绘制,也不会再render函数中出现任何一句openGL函数,它调用一个固定的函数,这个函数会在下一篇文章中介绍。
      GROUP_COMMAND:GroupCommand类绘制组,一个节点包括两个以上绘制命令的时候,把这个绘制命令存储到另外一个_renderGroups中的元素中,并把这个元素的指针作为一个节点存储到_renderGroups[0]中。


       第三,如何在Cocos2d-x中自己设置渲染功能。

       1.第一种方法针对的是整个图层的渲染。

        重写visit()函数,并且在visit()函数中直接向CommandQueue添加CustomCommand,设置好回调函数,这个比较直接,如代码14,代码14是子龙山人《基于Cocos2d-x学习OpenGL ES 2.0》第一篇中的部分代码。或者重写draw()函数,并且在draw()函数中向CommandQueue添加CustomCommand,设置好回调函数,这个就比较按照正规的流程走。

        代码14:

        void HelloWorld::visit(cocos2d::Renderer *renderer, const Mat4 &transform, bool transformUpdated)  
        {  
            Layer::draw(renderer, transform, transformUpdated);  
              
            //send custom command to tell the renderer to call opengl commands  
            _customCommand.init(_globalZOrder);  
            _customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);  
            renderer->addCommand(&_customCommand);  
              
              
        }  
        void HelloWorld::onDraw()  
        {  
            //question1: why the triangle goes to the up side  
            //如果使用对等矩阵,则三角形绘制会在最前面  
            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);  
             
            auto glProgram = getGLProgram();  
              
            glProgram->use();  
              
            //set uniform values, the order of the line is very important  
            glProgram->setUniformsForBuiltins();  
            auto size = Director::getInstance()->getWinSize();  
              
            //use vao  
            glBindVertexArray(vao);  
              
            GLuint uColorLocation = glGetUniformLocation(glProgram->getProgram(), "u_color");  
              
            float uColor[] = {1.0, 1.0, 1.0, 1.0};  
            glUniform4fv(uColorLocation,1, uColor);     
        //  glDrawArrays(GL_TRIANGLES, 0, 6);  
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE,(GLvoid*)0);    
            glBindVertexArray(0);   
            CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 6);  
            CHECK_GL_ERROR_DEBUG();    
            Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);     
            Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);  
          
        }  

             从代码14中,我们看到重写visit()函数,在visit()函数中直接向RenderQueue添加RenderCommand,即“renderer->addCommand(&_customCommand);”,由于此RenderCommand类型为CustomCommand,所以要添加处理图形渲染的回调函数,即“_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);”,这行代码就是添加回调函数的,onDraw()函数中调用OpengGL的API渲染图形。关于func是如何被调用,可以参考上面的代码12上下文的分析。

         2.第二种方法针对个别精灵。

         有时候,我们只要对个别精灵进行特效的处理,这个精灵需要使用我们自己编写的Shader,而图层其他的元素按默认处理就行了。这时候就需要第二种方法了。设置好Shader,向精灵添加Shader,最后在重写draw函数,在draw函数中进行特效的处理,如代码15,代码15是《捕鱼达人3》教程第二节的代码。

         代码15:

        bool FishLayer::init()  
        {  
                 ...省略了不相关的代码。  
            // 将vsh与fsh装配成一个完整的Shader文件。  
            auto glprogram = GLProgram::createWithFilenames("UVAnimation.vsh", "UVAnimation.fsh");  
            // 由Shader文件创建这个Shader  
            auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);  
            // 给精灵设置所用的Shader  
            m_Sprite->setGLProgramState(glprogramstate);  
          
            //创建海龟所用的贴图。  
            auto textrue1 = Director::getInstance()->getTextureCache()->addImage("tortoise.png");  
            //将贴图设置给Shader中的变量值u_texture1  
            glprogramstate->setUniformTexture("u_texture1", textrue1);  
            //创建波光贴图。  
            auto textrue2 = Director::getInstance()->getTextureCache()->addImage("caustics.png");  
            //将贴图设置给Shader中的变量值u_lightTexture  
            glprogramstate->setUniformTexture("u_lightTexture", textrue2);  
          
            //注意,对于波光贴图,我们希望它在进行UV动画时能产生四方连续效果,必须设置它的纹理UV寻址方式为GL_REPEAT。  
            Texture2D::TexParams    tRepeatParams;  
            tRepeatParams.magFilter = GL_LINEAR_MIPMAP_LINEAR;  
            tRepeatParams.minFilter = GL_LINEAR;  
            tRepeatParams.wrapS = GL_REPEAT;  
            tRepeatParams.wrapT = GL_REPEAT;  
            textrue2->setTexParameters(tRepeatParams);  
            //在这里,我们设置一个波光的颜色,这里设置为白色。  
            Vec4  tLightColor(1.0,1.0,1.0,1.0);  
            glprogramstate->setUniformVec4("v_LightColor",tLightColor);  
            //下面这一段,是为了将我们自定义的Shader与我们的模型顶点组织方式进行匹配。模型的顶点数据一般包括位置,法线,色彩,纹理,以及骨骼绑定信息。而Shader需要将内部相应的顶点属性通道与模型相应的顶点属性数据进行绑定才能正确显示出顶点。  
            long offset = 0;  
            auto attributeCount = m_Sprite->getMesh()->getMeshVertexAttribCount();  
            for (auto k = 0; k < attributeCount; k++) {  
                auto meshattribute = m_Sprite->getMesh()->getMeshVertexAttribute(k);  
                glprogramstate->setVertexAttribPointer(s_attributeNames[meshattribute.vertexAttrib],  
                                                     meshattribute.size,  
                                                     meshattribute.type,  
                                                     GL_FALSE,  
                                                     m_Sprite->getMesh()->getVertexSizeInBytes(),  
                                                     (GLvoid*)offset);  
                offset += meshattribute.attribSizeBytes;  
            }  
          
            //uv滚动初始值设为0  
            m_LightAni.x = m_LightAni.y = 0;  
            return true;  
        }  
          
        void FishLayer::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags)  
        {  
            if(m_Sprite)  
            {  
                //乌龟从右向左移动,移出屏幕后就回到最右边  
                auto s = Director::getInstance()->getWinSize();  
                m_Sprite->setPositionX(m_Sprite->getPositionX()-1);  
                if(m_Sprite->getPositionX() < -100)  
                {  
                    m_Sprite->setPositionX(s.width + 10);  
                }  
                  
                auto glprogramstate = m_Sprite->getGLProgramState();  
                if(glprogramstate)  
                {  
                    m_LightAni.x += 0.01;  
                    if(m_LightAni.x > 1.0)  
                    {  
                        m_LightAni.x-= 1.0;  
                    }  
                    m_LightAni.y += 0.01;  
                    if(m_LightAni.y > 1.0)  
                    {  
                        m_LightAni.y-= 1.0;  
                    }  
                    glprogramstate->setUniformVec2("v_animLight",m_LightAni);  
                }  
            }  
            Node::draw(renderer,transform,flags);  
        }  


        从代码15中,我们可以看到先使用OpengGL的API创建自己的Shader,然后再把m_sprite的Shader设置为自己的Shader即“m_Sprite->setGLProgramState(glprogramstate);,这是给精灵设置所用的Shader,这就是针对个别的精灵,而不是整个图层。接着在draw()中,如果精灵已生成,每次调用draw()函数都改变Shader中参数,以达到特别的效果。
        以上都是我通过阅读别人的代码总结的方法,不知道还有没有其他的在Cocos2d-x中自己设置渲染功能的方法,如果有的话,请告诉我,直接在我的博客留言就可以了。

       参考资料:

       1.http://cn.cocos2d-x.org/article/index?type=wiki&url=/doc/cocos-docs-master/manual/framework/native   /wiki/renderer/zh.md

        2.http://cocos2d-x.org/wiki/Cocos2d_v30_renderer_pipeline_roadmap

        3.http://cn.cocos2d-x.org/tutorial/show?id=1336

        4.http://blog.csdn.net/bill_man/article/details/35839499

        5.《Cocos2d-x高级开发教程》2.1.4节

        6.Cocos2d-x3.2和Cocos2d-x2.0.4源码

        如需转载,请标明出处:http://blog.csdn.net/cbbbc/article/details/39449945


    /************************************************************************************************************************/

      上一篇文章介绍了cocos2d-x的基本渲染结构,这篇顺着之前的渲染结构介绍渲染命令QUAD_COMMAND命令的部分,通过这部分的函数,学习opengl处理图片渲染的方法,首先介绍这节需要涉及到的基本概念VAO和VBO。

    VAO和VBO

        顶点数组对象(Vertex Array Object  即VAO)是一个包含一个或数个顶点缓冲区对象(Vertex Buffer Object, 即 VBO)的对象,一般存储一个可渲染物体的所有信息。顶点缓冲区对象(VertexBuffer Object VBO)是你显卡内存中的一块高速内存缓冲区,用来存储顶点的所有信息。

    这些概念显得很晦涩,简而言之,一般我们绘制一些图形需要将所有顶点的信息存储在一个数组里,但是经常会出现一些点是被重复使用的,这样就会出现一个点的信息的存储空间被重复使用的问题,这样第一会造成存储控件的浪费,第二就是如果我们要修改这个点的信息,需要改多次。所以我们采用索引的方式来描述图形,这样可以用一个数组存储点的信息,另外一个数组存储点的索引,这样所有的点都是不同的,另外把顶点信息存储在显卡的内存中,减少了cpu向gpu传输数据的时间,提高了程序的渲染效率,这就是VBO,在OpenGL3.0中,出现了更进一步的VAO,VBO通过绘制上下文获得绘制状态,VAO可以拥有多个VBO,它记录所有绘制状态,它的代码更简洁,效率更高,在cocos2d-x的绘制中,我们会判断底层是否支持VAO,如果支持VAO,那么优先采用VAO绘制。二者的区别可以从初始化就可以看出来:

        void Renderer::setupBuffer()  
        {  
            if(Configuration::getInstance()->supportsShareableVAO())  
            {  
                //初始化VBO和VAO  
                setupVBOAndVAO();  
            }  
            else  
            {  
                //不支持VAO,只初始化VBO  
                setupVBO();  
            }  
        }  
        void Renderer::setupVBOAndVAO()  
        {  
            //一个VAO  
            glGenVertexArrays(1, &_quadVAO);  
            //绑定VAO  
            GL::bindVAO(_quadVAO);  
            //创建生成两个VBO  
            glGenBuffers(2, &_buffersVBO[0]);  
            //顶点Buffer  
            glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
            glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * VBO_SIZE, _quads, GL_DYNAMIC_DRAW);  
            //这里就是VAO和VBO的区别,VAO把这些放到初始化中,无论后面绘制多少次,只要他不被改变,这段代码只会被调用一次,而VBO中,这个功能的代码会在每次被绘制时调用,这样就节约了效率  
            //位置  
            glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, vertices));  
            //颜色  
            glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR);  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, colors));  
            //纹理坐标数据  
            glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, texCoords));  
            //索引Buffer  
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);  
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * VBO_SIZE * 6, _indices, GL_STATIC_DRAW);  
            //取消VAO  
            GL::bindVAO(0);  
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
            glBindBuffer(GL_ARRAY_BUFFER, 0);  
          
            CHECK_GL_ERROR_DEBUG();  
        }  
        void Renderer::setupVBO()  
        {  
            //创建生成两个VBO  
            glGenBuffers(2, &_buffersVBO[0]);  
            //调用函数绑定buffer  
            mapBuffers();  
        }  
        void Renderer::mapBuffers()  
        {  
            //GL_ARRAY_BUFFER 表示顶点数据  
            //GL_ELEMENT_ARRAY_BUFFER 表示索引数据  
            //避免改变buffer元素  
            GL::bindVAO(0);  
            //绑定id 顶点数据  
            glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
            //为改id制定一段内存区域  
            glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * VBO_SIZE, _quads, GL_DYNAMIC_DRAW);  
            glBindBuffer(GL_ARRAY_BUFFER, 0);  
            //第二个VBO 索引数据  
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);  
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * VBO_SIZE * 6, _indices, GL_STATIC_DRAW);  
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
          
            CHECK_GL_ERROR_DEBUG();  
        }  


    需要介绍的两个关键的函数

    glBindBuffer:它绑定缓冲区对象表示选择未来的操作将影响哪个缓冲区对象。如果应用程序有多个缓冲区对象,就需要多次调用glBindBuffer()函数:一次用于初始化缓冲区对象以及它的数据,以后的调用要么选择用于渲染的缓冲区对象,要么对缓冲区对象的数据进行更新。

    当传入的第二个参数第一次使用一个非零无符号整数时,创建一个新的缓冲区对象;当第二个参数是之前使用过的,这个缓冲区对象成为活动缓冲区对象;如果第二个参数值为0时,停止使用缓冲区对象

    glBufferData:保留空间存储数据,他分配一定大小的(第二个参数)的openGL服务端内存,用于存储顶点数据或索引。这个被绑定的对象之前相关联的数据都会被清除。

    glBufferData参数介绍

    参数1,目标GL_ARRAY_BUFFER或者GL_ELEMENT_ARRAY_BUFFER

    参数2,内存容量

    参数3,用于初始化缓冲区对象,可以使一个指针,也可以是空

    参数4,如何读写,可以选择如下几种

        GL_DYNAMIC_DRAW:多次指定,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据不仅常常需要进行更新,而且使用频率也非常高

        GL_STATIC_DRAW:数据只指定一次,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据只指定1次,但是这些数据被使用的频率很高

        GL_STREAM_DRAW:数据只指定一次,最多只有几次作为绘图和图像指定函数的源数据,缓冲区对象中的数据常常需要更新,但是在绘图或其他操作中使用这些数据的次数较少

    从初始化的代码上,为什么VAO反倒复杂了呢?因为他只是把绘制时需要做的一些事情提前放到初始化函数中,来看一下绘制流程。

        //当前的openGL是否支持VAO  
        if (Configuration::getInstance()->supportsShareableVAO())  
        {  
            //绑定顶点数组  
            glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
            //向缓冲区申请空间并指定数据传输方式  
            glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * (_numQuads), nullptr, GL_DYNAMIC_DRAW);  
            //提供缓冲区对象包含整个数据集合的更新  
            void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);  
            memcpy(buf, _quads, sizeof(_quads[0])* (_numQuads));  
            //缓冲区对象的更新完成  
            glUnmapBuffer(GL_ARRAY_BUFFER);  
            //为了禁用缓冲区对象,可以用0作为缓冲区对象的标识符来调用glBindBuffer()函数。这将把OpenGL切换为默认的不使用缓冲区对象的模式。  
            glBindBuffer(GL_ARRAY_BUFFER, 0);  
            //Bind VAO  
            GL::bindVAO(_quadVAO);  
        }  
        else  
        {  
        ine kQuadSize sizeof(_quads[0].bl)  
            glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);  
            glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _numQuads , _quads, GL_DYNAMIC_DRAW);  
            //激活顶点颜色纹理坐标的属性  
            GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);  
            //顶点  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices));  
            //颜色  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors));  
            //纹理坐标  
            glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords));  
          
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);  
        }

    可以看到,这些设置属性的函数放在了绘制函数里,虽然看似是一样的,但是绘制函数会被调用的更频繁,所以把这些函数放到初始化函数中可以大幅提高程序的效率。

    这里介绍VAO的两个函数:

    glMapBuffer函数返回一个指针,指向与第一个参数相关联的当前绑定缓冲区对象的数据存储。第一个参数与glBufferData的第一个参数一致。第二个参数是GL_READ_ONLY、GL_WRITE_ONLY或GL_READ_WRITE之一,表示可以对数据进行的操作。

    glUnmapBuffer表示对当前绑定缓冲区对象的更新已经完成,并且这个缓冲区可以释放。

    enableVertexAttribs激活相关属性,激活的属性可以调用glVertexAttribPointer指定数据源,可选的有VERTEX_ATTRIB_FLAG_POSITION,VERTEX_ATTRIB_FLAG_COLOR和VERTEX_ATTRIB_FLAG_TEX_COORDS,这里这个参数是激活这三个。

    glVertexAttribPointer指定了渲染时第一个参数代表的索引值的顶点属性数组的数据格式和位置。

    第一个参数指定要修改的顶点属性的索引值,包括VERTEX_ATTRIB_POSITION(位置),VERTEX_ATTRIB_COLOR(颜色),VERTEX_ATTRIB_TEX_COORDS(纹理坐标)。

    第二个参数指定每个属性值的组件数量且必须为1、2、3、4之一。

    第三个参数指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT,GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。

    第四个参数指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE,意味着整数型的值会被映射至区间[-1,1](有符号整数),或者区间[0,1](无符号整数))或者直接转换为固定点值(GL_FALSE)。

    第五个参数指定了一个属性到下一个属性之间的步长(这就允许属性值被存储在单一数组或者不同的数组中)。也就是连续顶点属性之间的偏移量。如果为0,那么它们是紧密排列在一起的。初始值为0。

    第六个参数指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0。

    最后需要调用绘制元素函数,绘制这些信息

        glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );  
    它根据索引绘图(注意:顶点数据和索引各自使用不同的缓冲区)

        需要注意的是在Renderer的析构函数中要调用glDeleteBuffers来释放它的资源,并使它的标识可以其他缓冲区对象使用。

    上一篇中介绍的几种渲染命令中的QUAD_COMMAND(这里把它称作四边形绘制)命令回调用drawBatchedQuads调用绘制函数,处理这个逻辑的命令是这样的:

        if(commandType == RenderCommand::Type::QUAD_COMMAND)  
        {  
            auto cmd = static_cast<QuadCommand*>(command);  
            CCASSERT(nullptr!= cmd, "Illegal command for RenderCommand Taged as QUAD_COMMAND");  
                              
            //如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制  
            if(_numQuads + cmd->getQuadCount() > VBO_SIZE)  
            {  
                CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() < VBO_SIZE, "VBO is not big enough for quad data, please break the quad data down or use customized render command");  
                drawBatchedQuads();  
            }  
            //将命令缓存起来,先不调用绘制  
            _batchedQuadCommands.push_back(cmd);  
            memcpy(_quads + _numQuads, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());  
            //转换成世界坐标  
            convertToWorldCoordinates(_quads + _numQuads, cmd->getQuadCount(), cmd->getModelView());  
            //记录下四边形数量  
            _numQuads += cmd->getQuadCount();  
        }  

     

    void Renderer::flush()  
    {  
        //绘制  
        drawBatchedQuads();  
        //清空  
        _lastMaterialID = 0;  
    } 


    这个处理主要是把命令存入_batchedQuadCommands中,如果如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制

    如果一直没有超过VBO的大小,drawBatchedQuads绘制函数将在flush被调用时调用


    如有错误,欢迎指出

    [转自]http://blog.csdn.net/bill_man/article/details/38314077


    /********************************************************************************************************************************************/

    上一篇介绍了QUAD_COMMAND渲染命令,顺带介绍了VAO和VBO,这一篇介绍批处理渲染命令BatchCommand,批处理命令的处理在Render中比较简单

        else if(commandType == RenderCommand::Type:: BATCH_COMMAND)  
        {  
             //将之前缓存的绘制  
             flush();  
             auto cmd = static_cast<BatchCommand*>(command);  
             //调用命令  
        cmd->execute()  
        }  


    首先调用flush将之前缓存的VBO绘制出来,然后调用命令自己的执行函数,这个过程和CUSTOM_COMMAND是一样的,不同的是这里的execute调用的是BatchCommand确定的绘制函数,CUSTOM_COMMAND调用是传入的func函数,这里看一下BatchCommand的execute函数。

        void BatchCommand::execute()  
        {  
            //设置渲染材质  
            //shader  
            _shader->use();  
            //先取得MV和P,然后将MV和P相乘得到MVP,即模型视图投影矩阵  
            _shader->setUniformsForBuiltins(_mv);  
            //选择纹理单元,建立一个绑定到目标纹理的有名称的纹理  
            GL::bindTexture2D(_textureID);  
            //混合  
            GL::blendFunc(_blendType.src, _blendType.dst);  
            //绘制  
            _textureAtlas->drawQuads();  
        }  


    首先介绍其中一个重要的概念MVP,MVP即模型(Model)、视图(View)、投影(Projection)。

    模型矩阵:所有物体都处于世界空间中,所以绘制和做变换的前提是把物体由模型空间转换到世界空间中,而这个转换就需要乘以模型矩阵。

    视图矩阵:下一步需要把世界空间转换到相机空间或者是视角空间中,这需要乘以视图矩阵。

    投影矩阵:最后需要将3d的立方体投影到一个2d平面上,需要从观察坐标系到齐次坐标系,需要乘以投影矩阵。整个过程如下图所示:



    setUniformsForBuiltins函数

    void GLProgram::setUniformsForBuiltins(const kmMat4 &matrixMV)  
    {  
        //通过flag中的变量判断是否使用了相关的矩阵,这些标志变量在updateUniforms被设置  
        kmMat4 matrixP;  
          
        kmGLGetMatrix(KM_GL_PROJECTION, &matrixP);  
      
        if(_flags.usesP)  
            setUniformLocationWithMatrix4fv(_uniforms[UNIFORM_P_MATRIX], matrixP.mat, 1);  
      
        if(_flags.usesMV)  
            setUniformLocationWithMatrix4fv(_uniforms[UNIFORM_MV_MATRIX], matrixMV.mat, 1);  
      
        if(_flags.usesMVP) {  
            kmMat4 matrixMVP;  
            kmMat4Multiply(&matrixMVP, &matrixP, &matrixMV);  
            setUniformLocationWithMatrix4fv(_uniforms[UNIFORM_MVP_MATRIX], matrixMVP.mat, 1);  
        }  
      
        if(_flags.usesTime) {  
            Director *director = Director::getInstance();  
            //这里不会使用真实的每帧时间去设置,那样太耗费效率  
            float time = director->getTotalFrames() * director->getAnimationInterval();  
              
            setUniformLocationWith4f(_uniforms[GLProgram::UNIFORM_TIME], time/10.0, time, time*2, time*4);  
            setUniformLocationWith4f(_uniforms[GLProgram::UNIFORM_SIN_TIME], time/8.0, time/4.0, time/2.0, sinf(time));  
            setUniformLocationWith4f(_uniforms[GLProgram::UNIFORM_COS_TIME], time/8.0, time/4.0, time/2.0, cosf(time));  
        }  
          
        if(_flags.usesRandom)  
            setUniformLocationWith4f(_uniforms[GLProgram::UNIFORM_RANDOM01], CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());  
    }

    这个函数里,有一个频繁出现的变量名uniforms,uniform是一种和着色器相关的,它必须被声明为全局变量,用于整个图元批次向保持不变的着色器传递数据,对于顶点着色器来说,可能最普遍的统一值就是变换矩阵,它通过调用glUniformXXXX系列函数向着色器传递数据,这里这个函数被封装了,调用setUniformLocationXXXX系列函数可以设置uniforms,这个函数首先通过调用updateUniformLocation函数判断这个uniforms的值是否确实被更新(在一个hash表里存上曾经设置过的键值对),如果确实需要被更新再调用glUniformXXXX更新这个uniforms,节约效率

    bindTexture2DN函数主要是调用glActiveTexture和glBindTexture做贴图的绑定

        void bindTexture2DN(GLuint textureUnit, GLuint textureId)  
        {  
        #if CC_ENABLE_GL_STATE_CACHE  
            //做缓存,那么就不用为相同的textureId重复调用了  
            CCASSERT(textureUnit < kMaxActiveTexture, "textureUnit is too big");  
            if (s_currentBoundTexture[textureUnit] != textureId)  
            {  
                s_currentBoundTexture[textureUnit] = textureId;  
                activeTexture(GL_TEXTURE0 + textureUnit);  
                glBindTexture(GL_TEXTURE_2D, textureId);  
            }  
        #else  
              
            glActiveTexture(GL_TEXTURE0 + textureUnit);  
            glBindTexture(GL_TEXTURE_2D, textureId);  
        #endif  
        }  

    glActiveTexture选择一个纹理单元,线面的纹理函数将作用于该纹理单元上,参数为符号常量GL_TEXTUREi ,i的取值范围为0~N-1,N是opengl实现支持的最大纹理单元数,这里,因为批处理只有一个纹理,所以textureUnit一直是0,由于使用缓存,只会被调用一次

    glBindTexture建立一个绑定到目标纹理的有名称的纹理。比如把一个贴图绑定到一个形状上。

    最后调用的绘制函数和上一篇介绍的类似,另外关于shader和混合的问题,将在后面单独开文章介绍

    在引擎中使用BatchCommand这个命令的地方有两处ParticleBatchNode和SpriteBatchNode,使用方法很简单。

        _batchCommand.init(  
                               _globalZOrder,  
                               _shaderProgram,  
                               _blendFunc,  
                               _textureAtlas,  
                               transform);  
        renderer->addCommand(&_batchCommand);  

    需要说明的是,在3.0之后的版本中,由于添加了auto-batch功能,ParticleBatchNode和SpriteBatchNode的节约效率的功能已经不那么明显,但是3.0之前的版本中,把精灵放在SpriteBatchNode父节点上和将粒子系统放在ParticleBatchNode,是能够把相同的精灵批处理,对于相同的贴图只调用一次绘制函数,还是对提升效率很有帮助的。

    如有错误,欢迎指出



    展开全文
  • cocos2dx从3.0开始自己封装了物理引擎,当然是基于chipmunk。相对box2d来讲,chipmunk更轻量。 下面直接上一些运用的规则: 1. 建立物理世界 Scene* HelloWorld::createScene() {  auto scene = Scene::...

    cocos2dx从3.0开始自己封装了物理引擎,当然是基于chipmunk。相对box2d来讲,chipmunk更轻量。


    下面直接上一些运用的规则:

    1. 建立物理世界

    Scene*HelloWorld::createScene()

    {

        auto scene =Scene::createWithPhysics();

        scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

        auto layer =HelloWorld::create();

        scene->addChild(layer);

        return scene;

    }


    与普通的Scene创建方式不同的是,物理世界调用了Scene的createWithPhysics()方法,其他一样。

    为了调试方便,可以给物理世界的刚体开启红边,这样就能直观的看出刚体了。


    2.建立边界

        Size visibleSize =Director::getInstance()->getVisibleSize();

        auto body =PhysicsBody::createEdgeBox( visibleSize,PHYSICSBODY_MATERIAL_DEFAULT, 3);

       auto edgeShape = Node::create();

        edgeShape->setPhysicsBody( body);

        edgeShape->setPosition( visibleSize.width /2, visibleSize.height /2);

       addChild( edgeShape);


    一般在Scene的init方法里创建物理世界的边界,方法是先调用PhysicsBody::createEdgeBox()创建一个边界刚体,然后再创建一个空的Node,将两个绑定在一起放在Scene中。

    这个逻辑我觉得很奇怪,为什么不在第一步创造物理世界的时候指定边界尼?


    3.接下来就是创造你需要的刚体了。

    一般都是创建好刚体,然后和精灵绑定。为了更符合精灵的形状以便于做碰撞检测,我们可以使用一些物理编辑器。

        auto sprite =Sprite::create("sprite.png");

       auto box = PhysicsBody::createBox( sprite->getContentSize());

        sprite->setPhysicsBody(box);

        addChild(sprite);

    这一段绑定刚体的代码还是很符合正常人逻辑的。我们也可以设置好多刚体的属性,例如重力等。


    4.刚体的三个掩码,都是32位:

    categoryBitmask:  定义了分类,只要and操作后非0就可以触发碰撞,默认全打开
    contactTestBitmask:定义了是否触发回调函数,默认关掉
    collisionBitmask:定义了发生碰撞后是否继续作用物理效果,如果不作用就会穿过去,作用就会摊开,默认全打开


    这三个掩码用来定义刚体,方便做碰撞检测。比如说,你肯定不希望英雄和自己发出的子弹做出碰撞检测吧,那就用这三个掩码来定义你的英雄和子弹。

    我在实测的时候发现没定义的情况下有问题:只给一个刚体定义了contactTestBitmask,其他没变。按道理来讲,这个时候两个刚体是能碰撞但是不会触发回调的。因为categoryBitmask全打开会碰撞,而一个contactTestBitmask是关掉的,也就是说这个关掉的contactTestBitmask和另一个定义的contactTestBitmask进行and操作后肯定还是0,所以不会触发回调。但是实际情况却是触发回调了。所以使用的时候建议定义下三个掩码。


    5.碰撞检测:

        auto contactListener=EventListenerPhysicsContact::create();

        contactListener->onContactBegin =CC_CALLBACK_1(HelloWorld::callback,this);

        _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener,this);


        boolHelloWorld::callback(constPhysicsContact& contact)

        {

            log("%d == a", contact.getShapeA()->getBody()->getContactTestBitmask());

            log("%d == b", contact.getShapeB()->getBody()->getContactTestBitmask());

            return true;

        }


    获得碰撞刚体的代码其实也挺奇葩的:contact.getShapeA()->getBody()和contact.getShapeB()->getBody()。但是细细一想好像也没什么好的办法来获取发生碰撞的刚体。

    好了,下一步等有空测试了运动后,再更新吧。






    展开全文
  • Cocos2dx就不废话介绍了...尽管如今Cocos2dx早已去到3.x的稳定版本号,但之所以选用Cocos2dx 2.x版本号。主要是这方面的资料比較多,本来搞Cocos2dx的人就不多了,搞了也没几个人写写编程记录,再去毫无资料仅仅有一...

    Cocos2dx就不废话介绍了, 很火的游戏引擎。关键是它开源。能够免费下载、学习、开发。不用搞这么多激活的东西。

    以下以Cocos2dx 2.x为例说明这个平台的一些基本东西。尽管如今Cocos2dx早已去到3.x的稳定版本号,但之所以选用Cocos2dx 2.x版本号。主要是这方面的资料比較多,本来搞Cocos2dx的人就不多了,搞了也没几个人写写编程记录,再去毫无资料仅仅有一堆难以看懂的官方文档的Cocos2dx 3.x,基本上等于瞎整。

    首先,因为这引擎在Windows的主流平台是通过大家熟悉的C++写出来的。所以Cocos2dx 2.x的开发,你首先要准备Visual Studio 2010以上的版本号。假设你选用Cocos2dx 3.x还要支持准备Visual Studio 2012以上的版本号。已经不支持Visual Studio 2008了。这就没办法了,怪怪下载一个完整安装。网上一搜一堆。不说了。

    之后,因为Cocos2dx 2.x在当今站点的最后一版2.2.6不再提供InstallWizardForVS2010.js这鬼东西。须要用python语言创建新的project,因此你能够參看我之前的《【Python】Windows版本号的Python开发环境的配置,Helloworld,Python中文问题。输入输出、条件、循环、数组、类》(点击打开链接)先配好Python2.x的环境。

    然后,你才在Cocos2dx的中文官网(点击打开链接)。例如以下图,拉到最以下。下载Cocos2dx 2.x版本号。

    详细下载地址为:http://54.192.159.100/cocos2d-x-2.2.6.zip?fid=D2qKo-*f7vaAbUj7fijGQlgs5hzdkV4YAAAAAOeOX4E0-gk5fRKd*Y-Bb8j7lCvn&mid=666&threshold=150&tid=5D3FD9855047216E67D27C85E859FC2D&srcid=119&verno=1


    下载之后,得到一个cocos2d-x-2.2.6.zip,解压。这个解压位置就是你以后cocos2dx的开发环境、project的所在文件夹。和php是一样的,不好迁移。请小心选择。

    在你的cocos2dx的解压文件夹找到build-win32.bat这东西,双击执行。


    经历例如以下的一个漫长的编译过程:


    搞好之后,会出现例如以下的一个界面,cocos2dx官方提供的一大堆样例,有兴趣看看之后,能够直接关闭。


    至此。Cocos2dx的配置完毕。

    以下创建一个属于我们自己的Helloworld。不要用官方的样例。根本看不出什么端倪。

    利用命令行进入.\cocos2d-x-2.2.6\tools\project-creator这个目录。利用python命令,例如以下图,创建一个工程。

    create_project.py -project 你的工程目录名称 -package 包名 -language 开发语言(基本上为cpp,不排除有大神精通肥猪流的Lua,也不建议使用javascript开发!)


    在创建的过程中,可能会有如上图的报错,不用管,由于你创建的MyDemo工程已经成功出如今.\cocos2d-x-2.2.6\projects目录了,进入.\cocos2d-x-2.2.6\projects\MyDemo\proj.win32打开HelloCpp.sln,開始利用Cocos2dx引擎(框架)在Windows上开发游戏。

    你能够观察到在.\cocos2d-x-2.2.6\projects\MyDemo。出现proj.android、proj.ios等平台的目录。这里意味着。你能够在这些平台相同利用Cocos2dx引擎(框架)进行开发。

    不是说一个平台开发,多平台共同编译……


    打开HelloCpp.sln之后,等待Visual Studio 2010载入一大堆外部库之后,你能够看到例如以下的文件结构:


    首先。你要明确一个概念。Cocos2dx中最简单的Helloworld,是由一个叫Helloworld的场景、然后在这个Helloworld场景上放上一个Helloworld字符串所组成的。

    官方初始的Helloworld还在Helloworld这个场景中放上背景图片、关闭button等杂七杂八的东西。让Helloworld文件看起来比較复杂。

    我们先从上图的main.cpp与AppDelegate.cpp入手,先设置好程序的一些基本东西。

    首先是main.cpp。这东西的主函数就5行代码,将其关于程序标题与窗体尺寸的18、19行进行改动。例如以下:

    #include "main.h"
    #include "AppDelegate.h"
    #include "CCEGLView.h"
    
    USING_NS_CC;
    
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                           HINSTANCE hPrevInstance,
                           LPTSTR    lpCmdLine,
                           int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // create the application instance
        AppDelegate app;
        CCEGLView* eglView = CCEGLView::sharedOpenGLView();
        eglView->setViewName("HelloWorld");//改动程序执行的标题为Helloworld
        eglView->setFrameSize(1024, 768);//修成程序执行的尺寸为1024x768
        return CCApplication::sharedApplication()->run();
    }
    

    其余代码看不懂临时先不要管,就像你当初学C语言,不必在乎#include<stdio.h>与void main(){}是什么鬼,你首先要会改printf()中的内容。关于引入、函数这些东西。回头再来学。


    之后,还没有改完,对AppDelegate.cpp进行改动,把第22行的帧数调试信息关了。第25行的帧数有兴趣能够改下。只是改了也看不出效果。改动例如以下:

    #include "AppDelegate.h"
    #include "HelloWorldScene.h"
    
    USING_NS_CC;
    
    AppDelegate::AppDelegate() {
    
    }
    
    AppDelegate::~AppDelegate() 
    {
    }
    
    bool AppDelegate::applicationDidFinishLaunching() {
        // initialize director
        CCDirector* pDirector = CCDirector::sharedDirector();
        CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();
    
        pDirector->setOpenGLView(pEGLView);
    	
        // turn on display FPS
        pDirector->setDisplayStats(false);//关闭调试信息
    
        // set FPS. the default value is 1.0/60 if you don't call this
        pDirector->setAnimationInterval(1.0 / 60);//这里是设置游戏执行的帧数锁定为60,一般牛B机器、牛B的游戏都是锁这个帧数,30帧是低配
    
        // create a scene. it's an autorelease object
        CCScene *pScene = HelloWorld::scene();
    
        // run
        pDirector->runWithScene(pScene);
    
        return true;
    }
    
    // This function will be called when the app is inactive. When comes a phone call,it's be invoked too
    void AppDelegate::applicationDidEnterBackground() {
        CCDirector::sharedDirector()->stopAnimation();
    
        // if you use SimpleAudioEngine, it must be pause
        // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();
    }
    
    // this function will be called when the app is active again
    void AppDelegate::applicationWillEnterForeground() {
        CCDirector::sharedDirector()->startAnimation();
    
        // if you use SimpleAudioEngine, it must resume here
        // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
    }
    
    其余那些什么初始化代码之类。看不懂,先不要管,也不要改。

    之后,对整个程序的核心,管理HelloWorld这个场景的HelloWorldScene.cpp进行改动。放上属于我们自己的东西,去掉官方的东西。主要对HelloWorldScene.cpp中的初始化參数bool HelloWorld::init(){}进行改动。其余不要动。整个HelloWorldScene.cpp改动例如以下:

    #include "HelloWorldScene.h"
    
    USING_NS_CC;
    
    CCScene* HelloWorld::scene()
    {
        // 'scene' is an autorelease object
        CCScene *scene = CCScene::create();
        
        // 'layer' is an autorelease object
        HelloWorld *layer = HelloWorld::create();
    
        // add layer as a child to scene
        scene->addChild(layer);
    
        // return the scene
        return scene;
    }
    
    // on "init" you need to initialize your instance
    bool HelloWorld::init()
    {
    	//保留已经存在的初始化部分。不要进行改动,開始
        if ( !CCLayer::init() )
        {
            return false;
        }
        
        CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
        CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
        //保留已经存在的初始化部分。不要进行改动。结束
    	
    	
        CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 96);//声明一个标签文本Helloworld,其字体为Arial。字号为96
        pLabel->setPosition(ccp(origin.x + visibleSize.width/2,//设置标签文本的位置ccp是必须的。visibleSize.width/2为屏幕的中央。前面的origin.x是必须加上,用于多平台固定好位置
                                origin.y + visibleSize.height/2));//y部分同理
        this->addChild(pLabel, 1);//把这个标签文本Helloworld放到场景中。层叠关系为1,相当于css中的z-index,这个数字越大,越在上面
        return true;
    }
    
    
    void HelloWorld::menuCloseCallback(CCObject* pSender)
    {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    	CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    #else
        CCDirector::sharedDirector()->end();
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
    #endif
    #endif
    }
    

    利用Ctrl+F5执行。或者点击上方的执行button则看到例如以下效果:

    假设在编译的时候,遇到例如以下图的“LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏”错误:

    那不关Cocos2dx的问题,是因为电脑中有多个Visual Studio冲突。或者之前版本号的Visual Studio没有卸载干净。

    当前系统中存在两个cvtres.exe文件,版本号不同就会有问题了。

    我们让VS2010直接使用系统的cvtres.exe程序。

    重命名或删除:(vs2010安装的位置).\VC\bin\cvtres.exe

    如图,我就把cvtres.exe搞成_cvtres.exe完事。


    最后总结一下,Cocos2dx的Helloworld比起SSH算是简单多了。代码清晰。关键是注意好版本号,正如php5.5之后不支持windows2003这种问题,你必须搞清晰Visual Studio 2010仅仅能支持Cocos2dx 2.x之类的。就是安装可能有些繁琐,毕竟python环境的安装对于非常多人来说比較陌生。Visual Studio 2010的安装的耗时实在是无力吐槽了,再加上Cocos2dx 2.x的编译过程,就是比較久。最主要网上很多蛋疼的资料就不说了……



    转载于:https://www.cnblogs.com/jzssuanfa/p/6867327.html

    展开全文
  • 笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,...CSDN视频网址:http://edu.csdn.net/lecturer/144 历经一年的书稿终于要问世了,这本书主要是介绍了Cocos2dx中的3D图形学渲染技术以及模型加密算法实现,虽然

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

    CSDN视频网址:http://edu.csdn.net/lecturer/144

    历经一年的书稿终于要问世了,这本书主要是介绍了Cocos2dx中的3D图形学渲染技术以及模型加密算法实现,虽然Cocos在3D这块做的不完整,但是它对于初学者学习3D渲染非常有帮助。

    本书主要介绍Cocos2D-X 3X以上版本使用的3D图形学渲染技术,以及关于3D引擎的架构和模型加密等,全书共分12章,主要内容包括可编程流水线

    、OpenGL编程、矩阵变换算法、3D坐标系统、包围盒算法、3D架构设计、3D特效、Shader渲染、3D模型渲染、引擎的滤镜渲染、3D骨骼动画、3D模型加密。

    本书重点介绍3D引擎架构设计、Shader渲染、3D特效、3D模型渲染算法及模型骨骼动画。第12章介绍了3D模型加密算法,在游戏开发中对模型加密是必须要

    实现的。本书适合具备一定游戏开发经验的初学者和具备3D项目开发经验的游戏开发者阅读。

    书的主要目录如下:

    第1章 可编程流水线
    1.1 GPU功能介绍
    1.2 GPU编程语言
    1.3 GPU编程案例
    小结
    第2章 OpenGL编程
    2.1 OpenGL库介绍
    2.2  着色器介绍
    2 3 OpenGL属性
    2.4 OpenGL案例
    小结
    第3章 矩阵变换算法
    3.1 矩阵平移变换算法
    3 .2 矩阵旋转变换算法
    3.3 矩阵缩放变换算法
    小结
    第4章 3D坐标系统
    4.1 局部空间
    4..2世界空间
    4.3 观察空间
    4.4 裁剪空间
    4.5 正交投影
    4.6 透视投影
    小结
    第5章 包围盒算法
    5.1 OBB包围盒算法
    5.2 AABB包围盒算法
    小结
    第6章 3D架构设计
    6.1 3D框架组成
    6.2  3D核心模块
    6.2.1  CCSkybox天空盒案例
    6.2.2 CCFrustrum视景体案例
    6.2.3 CCBundle3D数据加载
    6.2.4 CCRay射线实现案例
    6.2.5 CCSprite3D类的作用
    6.2.6 CCAttachNode类实现换装
    6.2.7 CCMeshSkin网格蒙皮作用
    6.2.8 CCSprite3DMaterial材质加载
    小结
    第7章 3D特效
    7.1 3D特效组织架构
    7.2 3D特效渲染
    7.3 3D特效运行案例
    小结
    第8章 Shader渲染
    8.1 Shader框架
    8.2 GLProgram类功能
    8.3 VertexIndexData类功能
    8.4 MeshVertexIndexData类功能
    8.5 VertexIndexBuffer类功能
    8.6 Renderer渲染功能
    8.7 TextureAtlas图集功能
    8.8 Technique技术实现
    8.9 Pass通道处理
    8.10 Material材质揭秘
    8.11 Primitive类功能
    8.12 RenderState类功能
    8.13 Texture2D类实现
    8.14 Shader加载案例
    小结
    第9章 3D模型渲染
    9.1 3D模型介绍
    9.2 3D模型加载
    9.3 3D材质渲染
    9.4 材质高光、法线渲染
    9.5 材质反射渲染
    9.6 模型渲染案例
    小结
    第10章 引擎的滤镜渲染
    10.1 Bloom渲染效果
    10.2 Blur渲染效果
    10.3 LensFlare镜头眩光
    小结
    第11章 3D骨骼动画
    11.1 3D骨骼动画介绍
    11.2 3D骨骼动画制作规范
    11.3 3D骨骼动画加载案例
    小结
    第12章 3D模型加密
    12.1 3D模型加密方式
    12.2 3D模型加密代码编写
    12.3 3D模型加密案例
    小结

    最后附上书的封面:


    展开全文
  • 第一章 创建cocos2dx项目
  • 暑假期间,在家闲着无聊,再加上学校课题的需要,开始自学cocos2d-x,上网大体了解了一下,这东西大概就是个游戏引擎,说真的,我起初也就知道个名词,什么游戏引擎,但是一直不懂那到底是个什么东东,这几天试着看...
  • 学习cocos2dx3.x打包APK

    2014-12-05 23:50:42
    Cocos2d-x 3.2项目打包成apk安卓应用文件,搭建安卓环境的步骤有点繁琐,但搭建一次之后,以后就会非常快捷!(涉及到3.1.1版本的,请自动对应3.2版本,3.x版本的环境搭建都是一样的) 一、下载Android环境 ...
  • 虽然现在Cocos2dx早已去到3.x的稳定版本,但之所以选用Cocos2dx 2.x版本,主要是这方面的资料比较多,本来搞Cocos2dx的人就不多了,搞了也没几个人写写编程记录,再去毫无资料只有一堆难以看懂的官方文档的
  • Cocos2dx就不废话介绍了, 非常火的游戏引擎,关键是它开源,可以免费下载、学习、开发,不用搞这么多激活的东西。...虽然现在Cocos2dx早已去到3.x的稳定版本,但之所以选用Cocos2dx 2.x版本,主要是这方面的资料
  • 新建一个场景, 头文件导入cocos2d.h、ui\CocosGUI.h等,声明命名空间USING_NS_CC, 声明成员变量(例如:ui按钮、精灵等)和函数声明(例如:构造函数、析构函数、用于初始化的init()方法、cocos2d提供的宏方法...
  • ~~~~我的生活,我的点点滴滴!!    一直想系统的知道到底3.x2.x版本有哪些大的变化与区别,今天xtgf
  • 1111
  • 引言 程序截图:  有时候,你在做游戏时,可能需要一种方式来显示精灵的某一部分(就是添加遮罩啦)。  一种方式就是使用另外一张图片,叫做mask。你把mask图片中间设置成白色,白色区域是被mask图片的可见...
  • Cocos2d-x3.x安装指南

    2016-06-22 16:22:21
    最近趁着做毕设实验的空闲期,看了下cocos2d-x.3x开发,准备写一系列的博客,记录下自己的学习历程。 首先要安装代码编译开发环境,这里我使用的是VS2013。安装完了VS2013之后需要下载cocos2d-x,这里有很多版本,...
  •     babe系列文章链接   系列1—BabeLua入门 ...系列2—BabeLua编辑调试Lua脚本 ...系列3—BabeLua常用技巧 ...版本:cocos 3.10 ...以前只用过vs2015+cocos2d-x 3.9写过小游戏,新下载了3.10,想学习下Lua的使...
  • OpenGL本身是跨平台的,但是每个平台的开发环境并不一样,把OpenGL的代码放在cocos里面运行就方便多了。 下面就是一个最简单的示例,在cocos中调用OpenGL方法,将窗口设置成蓝色。 首先新建一个cocos项目,删掉不...
  • 最近做cocos2dx ,由于在window平台上运行的游戏,故出很多问题,图片模糊就是其中一个。 下面给出我的解决方案,(由于用的cocostudio 加载的图片文件,故只能改源码解决了)(个人建议还是最好不要改源码) ...
  • cocos2dx社区里有个系列博客完整地复制原版flappybird的所有特性,不过那个代码写得比较复杂,新手学习起来有点捉摸不透,这里我写了个简单的版本。演示如下: 创建项目 VS2013+cocos2dx 3.2创建win32项目,...
  • (3)cocos2d-X 3.x版本 (4)Python2.7(用3.0可能会出问题) 2.所需资源的下载网站 Python:https://www.python.org/downloads/ 进入官方网址,这里全是英文,不过不必担心。因为我们只需要知道Downloads单词是下载的...
1 2 3 4 5 ... 20
收藏数 5,595
精华内容 2,238
关键字:

2dx 3.x学习 cocos