公众号推荐
微信公众号搜"智元新知" 关注 微信扫一扫可直接关注哦!
Cocos2d-x3.2与OpenGL渲染总结Cocos2d-x3.2的渲染流程
最近几天,我都在学习如何在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:
void CCSprite::draw( void )
{
CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite,"CCSprite-draw" );
CCAssert(!m_pobBatchNode,"IfCCSpriteisbeingrenderedbyCCSpriteBatchNode,CCSprite#drawSHOULDNOTbecalled" );
CC_NODE_DRAW_SETUP();
ccGLBlendFunc(m_sBlendFunc.src,m_sBlendFunc.dst);
if(m_pobTexture!=NULL)
ccGLBindTexture2D(m_pobTexture->getName());
}
else
ccGLBindTexture2D(0);
//Attributes
ccGLEnabLever texAttribs(kCCVertexAttribFlag_PosColorTex);
#definekQuadSizesizeof(m_squad .bl)
long offset=( long )&m_squad ;
//vertex
intdiff=offseto f(ccV3F_C4B_T2F,vertices);
glVertexAttribPointer(kCCVertexAttrib_Position,3,GL_FLOAT,GL_FALSE,kQuadSize,(void *)(offset+diff));
//texCoods
diff=offseto f(ccV3F_C4B_T2F,texCoords);
glVertexAttribPointer(kCCVertexAttrib_TexCoords,2,153); font-weight:bold; background-color:inherit">void *)(offset+diff));
//color
diff=offseto f(ccV3F_C4B_T2F,colors);
glVertexAttribPointer(kCCVertexAttrib_Color,4,GL_UNSIGNED_BYTE,GL_TRUE,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> glDrawArrays(GL_TRIANGLE_STRIP,4);
CHECK_GL_ERROR_DEBUG();
#ifCC_SPRITE_DEBUG_DRAW==1
//drawboundingBox
CCPointvertices[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),108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ccp(m_squad .tr.vertices.x,m_squad .tr.vertices.y),248)"> };
ccDrawpoly (vertices,true );
#elifCC_SPRITE_DEBUG_DRAW==2
//drawtextureBox
CCSizes=this ->getTextureRect().size;
CCPointoffsetPix=this ->geto ffsetPosition();
CCPointvertices[4]={
ccp(offsetPix.x,offsetPix.y),ccp(offsetPix.x+s.width,248)"> ccp(offsetPix.x+s.width,offsetPix.y+s.height),ccp(offsetPix.x,offsetPix.y+s.height)
};
ccDrawpoly (vertices,153); font-weight:bold; background-color:inherit">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_tflags)
{
//Don'tdocalcula tethecullingifthetransformwasnotupdated
_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);
#ifCC_SPRITE_DEBUG_DRAW
_customDebugDrawCommand.init(_globalZOrder);
_customDebugDrawCommand.func=CC_CALLBACK_0(Sprite::drawDebugData,153); font-weight:bold; background-color:inherit">this );
renderer->addCommand(&_customDebugDrawCommand);
}
}
从代码 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:
intApplication::run()
...
director->mainLoop();
}
从代码 3中,它明显的启发着我们要继续追寻Director::mainLoop()函数 。在Director中mainLoop()为纯函数 ,此子类dis playLinkDirector才有其实现,如代码 4。
代码 4:
voiddis playLinkDirector::mainLoop()
<span><spanclass = "comment" >
if(_purgeDirectorInNextLoop)
_purgeDirectorInNextLoop=false ;
//清除导演类</span><span></span></span>
purgeDirector();
elseif (!_invalid)
{<span><span//绘制</span><span></span></span>
drawScene();
//清除当前内存池中对象,即池中每一个 对象--_referenceCount
PoolManager::getInstance()->getCurrentPool()->clear();
}
mainLoop是主线程调用 的循环,其中drawScene()是绘制函数 ,接着我们继续追寻它的代码 ,如代码 5。
代码 5:
voidDirector::drawScene()
//计算间隔时间</span><span></span></span>
calcula teDeltaTime();
//忽略该帧如果时间间隔接近0
if(_deltaTime<FLT_EPSILON)
return;
if(_openGLView)
_openGLView->pollInputEvents();
//tickbeforeglClear:issue#533
if(!_paused)
_scheduler->update(_deltaTime);
_eventdis patcher->dis patchEvent(_eventAfterUpdate);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
/*toavoidflickr,nextSceneMUSTbehere:aftertickandbeforedraw.
XXX:Whichbugisthisone.Itseemsthatitcan'tbereproducedwithv0.9*/
if(_nextScene)
setNextScene();
pushmatrix (MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//drawthescene
if(_runningScene)
_runningScene->visit(_renderer,Mat4::IDENTITY,153); font-weight:bold; background-color:inherit">false );
_eventdis patcher->dis patchEvent(_eventAfterVisit);
//drawthenotificationsnode
if(_notificationNode)
_notificationNode->visit(_renderer,153); font-weight:bold; background-color:inherit">false );
if(_dis playStats)
showStats();
_renderer->render();
_eventdis patcher->dis patchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
//swapbuffers
if(_openGLView)
_openGLView->swapBuffers();
calcula teMPF();
}
从代码 5中,我们看见visit()和render()函数 的调用 。其中visit()函数 会调用 draw()函数 来向RenderQueue中添加 RenderCommand,那么就继续追寻visit()的代码 ,如代码 6。
代码 6:
voidNode::visit(Renderer*renderer,153); font-weight:bold; background-color:inherit">const Mat4&parentTransform,uint32_tparentFlags)
//quickreturnifnotvisible.childrenwon'tbed rawn.
if(!_visible)
return;
uint32_tflags=processp arentFlags(parentTransform,parentFlags);
//IMPORTANT:
//Toeasethemigrationtov3.0,westillsupporttheMat4stack,
//butitisdeprecatedandyourcodeshouldnotrelyonit
Director*director=Director::getInstance();
director->pushmatrix (MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW,_modelViewTransform);
inti=0;
if(!_children.empty())
sortAllChildren();
//drawchildrenzOrder<0
for(;i<_children.size();i++)
autonode=_children.at(i);
if(node&&node->_localZOrder<0)
node->visit(renderer,_modelViewTransform,flags);
break;
//selfdr aw
this->draw(renderer,flags);
for(autoit=_children.cbegin()+i;it!=_children.cend();++it)
(*it)->visit(renderer,153); font-weight:bold; background-color:inherit">else
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//FIXME:Whyneedtoset_orderOfArrivalto0??
//Pleaserefertohttps://github.com/cocos2d/cocos2d-x/pull/6920
//resetfornextframe
//_orderOfArrival=0;
}
从代码 6中,我们可以看到“ auto node = _children.at(i);和node->visit(renderer,flags);”,这段代码 的意思是先获取 子节点,然后递归调用 节点的visit()函数 ,到了没有子节点的节点,开始调用 draw()函数 。那么我们看看draw()函数 代码 ,如代码 7。
代码 7:
voidNode::draw(Renderer*renderer,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> }
好吧,从代码 7中,我们看到Node的draw什么都没有做,是我们找错地方?原来draw()是虚函数 ,所以它执行时执行的是该字节类的draw()函数 。确实是我们找错地方了。那么我们分别看DrawNode::draw()、Sprite::draw()。
代码 8:
voidDrawNode::draw(Renderer*renderer,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> _customCommand.init(_globalZOrder);
_customCommand.func=CC_CALLBACK_0(DrawNode::onDraw,153); font-weight:bold; background-color:inherit">this ,transform,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> renderer->addCommand(&_customCommand);
//Don'tdocalcula tethecullingifthetransformwasnotupdated
_insideBounds=(flags&FLAGS_TRANSFORM_DIRTY)?renderer->checkVisibility(transform,_contentSize):_insideBounds;
}
从代码 8中,我们可以看到在draw()函数
向RenderQueue中添加 RenderCommand,当然有的类的draw()不是向RenderQueue中添加 RenderCommand ,而是直接使用OpenGL的API直接进行渲染,或者做一些其他的事情。
那么当draw()都递归调用 完了,我们来看看最后进行渲染的Renderer::render() 函数 ,如代码 9。
代码 9:
voidRenderer::render()
//UncommentthisonceeverythingisrenderedbyneWren derer
//glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//Todo setupcameraorMVP
_isRendering=true ;
if(_glViewAssigned)
//cleanup
_drawnBatches=_drawnVertices=0;
//Processrendercommands
//1.so rtrendercommandsbasedonID
for(auto&renderqueue:_renderGroups)
renderqueue.so rt();
visitRenderQueue(_renderGroups[0]);
flush();
clean();
_isRendering= }
从代码 9中,我们看到“renderqueue.so rt()",这是之前所说的对命令先排序,然后才进行渲染,“visitRenderQueue( _renderGroups[0])”就是来进行渲染的。那么我们接着看看void Renderer::visitRenderQueue(const RenderQueue& queue)的代码 ,如代码 10。
代码 10:
voidRenderer::visitRenderQueue( const RenderQueue&queue)
ssize_tsize=queue.size();
for(ssize_tindex=0;index<size;++index)
autocommand=queue[index];
autocommandType=command->getType();
if(RenderCommand::Type::QUAD_COMMAND==commandType)
flush3D();
autocmd=static_cast <QuadCommand*>(command);
//Batchquads
if(_numQuads+cmd->getQuadCount()>VBO_SIZE)
CCASSERT(cmd->getQuadCount()>=0&&cmd->getQuadCount()<VBO_SIZE,"VBOisnotbigenoughforquaddata,pleasebreakthequaddatadownor usecustomizedrendercommand" );
//DrawbatchedquadsifVBOisfull
drawBatchedQuads();
_batchedQuadCommands.push_back(cmd);
memcpy (_quads+_numQuads,cmd->getQuads(),153); font-weight:bold; background-color:inherit">sizeof (V3F_C4B_T2F_Quad)*cmd->getQuadCount());
convertToWorldCoordinates(_quads+_numQuads,cmd->getQuadCount(),cmd->getModelView());
_numQuads+=cmd->getQuadCount();
if(RenderCommand::Type::GROUP_COMMAND==commandType)
flush();
intrenderQueueID=((GroupCommand*)command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
if(RenderCommand::Type::CUSTOM_COMMAND==commandType)
autocmd=static_cast <CustomCommand*>(command);
cmd->execute();
if(RenderCommand::Type::BATCH_COMMAND==commandType)
static_cast<BatchCommand*>(command);
if(RenderCommand::Type::MESH_COMMAND==commandType)
flush2D();
static_cast<MeshCommand*>(command);
if(_lastBatchedMeshCommand==nullptr||_lastBatchedMeshCommand->getMaterialID()!=cmd->getMaterialID())
flush3D();
cmd->preBatchDraw();
cmd->batchDraw();
_lastBatchedMeshCommand=cmd;
cmd->batchDraw();
ccl OGERROR("UnkNow ncommandsinrenderQueue" );
}
从代码 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:
voidRenderer::drawBatchedQuads()
//Todo wecanimprovethedrawperformancebyinsertmaterialswitchingcommandbeforehand.
intquadsToDraw=0;
int startQuad=0;
//UploadbuffertoVBO
if(_numQuads<=0||_batchedQuadCommands.empty())
if(Config uration::getInstance()->supportsShareableVAO())
//SetVBOdata
glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]);
//option1:subdata
//glBufferSubData(GL_ARRAY_BUFFER,sizeof(_quads[0])*start,sizeof(_quads[0])*n,&_quads[start]);
//option2:data
//glBufferData(GL_ARRAY_BUFFER,sizeof(quads_[0])*(n-start),&quads_[start],GL_DYNAMIC_DRAW);
//option3:orphaning+glMapBuffer
glBufferData(GL_ARRAY_BUFFER,153); font-weight:bold; background-color:inherit">sizeof (_quads[0])*(_numQuads),nullptr,GL_DYNAMIC_DRAW);
void*buf=glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);
memcpy (buf,_quads,153); font-weight:bold; background-color:inherit">sizeof (_quads[0])*(_numQuads));
glu nmapBuffer(GL_ARRAY_BUFFER);
//BindVAO
GL::bindVAO(_quadVAO);
#definekQuadSizesizeof(_quads[0].bl)
glBufferData(GL_ARRAY_BUFFER,153); font-weight:bold; background-color:inherit">sizeof (_quads[0])*_numQuads,GL_DYNAMIC_DRAW);
GL::enabLever texAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
//vertices
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,(GLvoid*)offseto f(V3F_C4B_T2F,vertices));
//colors
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,colors));
//texcoords
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD,texCoords));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]);
//Startdrawingvertiesinbatch
for( const auto&cmd:_batchedQuadCommands)
autonewMaterialID=cmd->getMaterialID();
if(_lastMaterialID!=newMaterialID||newMaterialID==QuadCommand::MATERIAL_ID_DO_NOT_BATCH)
//Drawquads
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;
//Usenewmaterial
cmd->useMaterial();
_lastMaterialID=newMaterialID;
quadsToDraw+=cmd->getQuadCount();
//Drawanyremainingquad
if(quadsToDraw>0)
glDrawElements(GL_TRIANGLES,153); font-weight:bold; background-color:inherit">sizeof (_indices[0])));
_drawnBatches++;
_drawnVertices+=quadsToDraw*6;
//UnbindVAO
GL::bindVAO(0);
_batchedQuadCommands.clear();
_numQuads=0;
代码 12:
voidCustomCommand::execute()
if(func)
func();
}
代码 13:
voidBatchCommand::execute()
//Setmaterial
_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:
voidHelloWorld::visit(cocos2d::Renderer*renderer, bool transformUpdated)
Layer::draw(renderer,transformUpdated);
//sendcustomcommandtotelltherenderertocallopenglcommands
_customCommand.init(_globalZOrder);
_customCommand.func=CC_CALLBACK_0(HelloWorld::onDraw,248)"> renderer->addCommand(&_customCommand);
voidHelloWorld::onDraw()
//question1:whythetrianglegoestotheupside
//如果使用对等矩阵,则三角形绘制会在最前面
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);
autoglProgram=getGLProgram();
glProgram->use();
//setuniformvalues,theorderofthelineisve ryimportant
glProgram->setUniformsForBuiltins();
autosize=Director::getInstance()->getWinSize();
//usevao
glBindVertexArray(vao);
glu intuColorLocation=glGetUniforml ocation(glProgram->getProgram(),"u_color" );
floatuColor[]={1.0,1.0,1.0};
glu niform4fv(uColorLocation,uColor);
//glDrawArrays(GL_TRIANGLES,6);
glBindVertexArray(0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,6);
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:
boolFishLayer::init()
...省略了不相关的代码 。
//将vsh与fsh装配成一个 完整的Shader文件 。
autoglprogram=GLProgram::createWithFilenames("UVAnimation.vsh" , "UVAnimation.fsh" );
//由Shader文件 创建这个Shader
autoglprogramstate=GLProgramState::geto rCreateWithGLProgram(glprogram);
//给精灵设置所用的Shader
m_Sprite->setGLProgramState(glprogramstate);
//创建海龟所用的贴图。
autotextrue1=Director::getInstance()->getTextureCache()->addImage("tortoise.png" );
//将贴图设置给Shader中的变量值u_texture1
glprogramstate->setUniformTexture("u_texture1" ,textrue1);
//创建波光贴图。
autotextrue2=Director::getInstance()->getTextureCache()->addImage("caustics.png" );
//将贴图设置给Shader中的变量值u_lightTexture
glprogramstate->setUniformTexture("u_lightTexture" ,textrue2);
//注意,对于波光贴图,我们希望它在进行UV动画时能产生四方连续效果 ,必须设置它的纹理UV寻址方式为GL_REPEAT。
Texture2D::TexPara mstRepeatPara ms;
tRepeatPara ms.magFilter=GL_LINEAR_MIPMAP_LINEAR;
tRepeatPara ms.minFilter=GL_LINEAR;
tRepeatPara ms.wrapS=GL_REPEAT;
tRepeatPara ms.wrapT=GL_REPEAT;
textrue2->setTexPara meters(tRepeatPara ms);
//在这里 ,我们设置一个 波光的颜色,这里设置为白色。
Vec4tLightColor(1.0,1.0);
glprogramstate->setUniformVec4("v_LightColor" ,tLightColor);
//下面这一段,是为了将我们自定义 的Shader与我们的模型顶点组织方式进行匹配。模型的顶点数据一般包括 位置,法线,色彩,纹理,以及骨骼绑定信息。而Shader需要将内部相应的顶点属性 通道与模型相应的顶点属性 数据进行绑定才能正确显示 出顶点。
longoffset=0;
autoattributeCount=m_Sprite->getMesh()->getMeshVertexAttribCount();
for(autok=0;k<attributeCount;k++){
automeshattribute=m_Sprite->getMesh()->getMeshVertexAttribute(k);
glprogramstate->setVertexAttribPointer(s_attributeNames[meshattribute.vertexAttrib],248)"> meshattribute.size,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> meshattribute.type,248)"> GL_FALSE,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> m_Sprite->getMesh()->getVertexSizeInBytes(),248)"> (GLvoid*)offset);
offset+=meshattribute.attribSizeBytes;
//uv滚动初始值设为0
m_LightAni.x=m_LightAni.y=0;
returntrue ;
voidFishLayer::draw(Renderer*renderer,153); font-weight:bold; background-color:inherit">if (m_Sprite)
//乌龟从右向左移动,移出屏幕后就回到最右边
autos=Director::getInstance()->getWinSize();
m_Sprite->setPositionX(m_Sprite->getPositionX()-1);
if(m_Sprite->getPositionX()<-100)
m_Sprite->setPositionX(s.width+10);
autoglprogramstate=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,248)"> }
从代码 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://www.jb51.cc/article/p-svmuttyv-sw.html
原文地址:https://www.jb51.cc/cocos2dx/343221.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。