cocos2d-x 渲染

http://blog.xulingmin.com/cocos2d-x-%E6%B8%B2%E6%9F%93/

首页

分类 标签 OPENGL 链接 留言 电视剧 CPLUSPLUS CPPMAN CMAN 视频解析 关于 订阅 2014-04-28| 分类cocos2d-x| 标签cocos2d-x-2.2.3

本文主要针对于2.2.3 cocos2d-x渲染做一分析,TestCpp ios项目启动,⾸先运行的当然就是main.m这个类了啊

  
  
main(int argc, char *argv[]){ NSAutoreleasePool pool =[[ alloc] init]; retVal UIApplicationMain(argc argvnil@"AppController");[pool releasereturn retVal;}

现在跟进到AppController,这里的AppController并不是cocos2d-x中的AppController,而是ios项目中的,找到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法,在该方法中有这么一句话cocos2d::CCApplication::sharedApplication()->run(),好了咱得去找CCApplication了。

CCApplication run方法中

  • CCApplication::run()ifapplicationDidFinishLaunching()) CCDirectorCaller sharedDirectorCaller startMainLoop];//启动了线程} 0

    跟进到startMainLoop中

  • displayLink invalidatedisplayLink ;//实例化CADisplayLink[NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selectordoCaller:)];//回调doCaller:displayLink setFrameInterval:self.interval];displayLink addToRunLoop:[NSRunLoop currentRunLoop forMode:NSDefaultRunLoopMode];

    CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。

    在doCaller:这个回调方法中,也就这么一句话cocos2d::CCDirector::sharedDirector()->mainLoop(),终于找到了党,CCDirector熟悉了吧。话说CCDirector没有直接实现mainLoop()这个方法,而是请他小弟CCDisplayLinkDirector来实现的。

    CCDisplayLinkDirector的mainLoop()

  • m_bPurgeDirecotorInNextLoop)//m_bPurgeDirecotorInNextLoop在end()方法中设置为true m_bPurgeDirecotorInNextLoop false; purgeDirector();}else(! m_bInvalid) drawScene();//重点还是drawScene()这个方法 // release the objectsCCPoolManagersharedPoolManager()->pop();//每次回调都要清除下pool池

    接下来还是先讨论下关于CCDirector吧。在初始化CCDirector方法中,真正的初始化值是sSharedDirector,也就是说在CCDirecor包裹了一成皮,里面真正干事的是它的子类CCDisplayLinkDirector,`static CCDisplayLinkDirector *sSharedDirector = NULL;`

    CCDirecotr的sharedDirector(void)方法

  • s_SharedDirector s_SharedDirector newCCDisplayLinkDirector s_SharedDirector->init//实现是在CCDirector中//真正返回的是CCDisplayLinkDirector

    跟进到CCDirecotr的init()方法

  • setDefaultValues//设置默认初始值,从CCConfiguration中读取初始值// 关于scene相关属性m_pRunningScene NULL//正在运行的场景m_pNextScene m_pNotificationNode //可以通过该Node设置悬浮窗或者过渡的节点,该节点不在场景中draw,是单独出来画的m_pobScenesStack CCArraym_pobScenesStack//初始化保存scene的栈// projection delegate if "Custom" projection is usedm_pProjectionDelegate //CCDirectorDelegate类只有当ccDirectorProjection为kCCDirectorProjectionCustom时候才实现运行该类// FPSm_fAccumDt 0.0fm_fFrameRate m_pFPSLabel m_pSPFLabel m_pDrawsLabel m_uTotalFrames m_uFrames m_pszFPS char[10m_pLastUpdate struct cc_timevalm_fSecondsPerFrame // paused ?m_bPaused //在CCDirector::pause(void)设置为true,CCDirector::resume(void)设置为false// purge ?m_bPurgeDirecotorInNextLoop //CCDirector::end()中设置为truem_obWinSizeInPoints CCSizeZerom_pobOpenGLView //CCEGLViewm_fContentScaleFactor 1.0f//缩放因子// schedulerm_pScheduler CCScheduler//CCScheduler定时调度器,// action managerm_pActionManager CCActionManager//Action的管理器m_pSchedulerscheduleUpdateForTargetm_pActionManager kCCPrioritySystem);//将CCActionManager根据nPriority>0,-0,<0分别放到_listEntry类型的m_pUpdatesPosList,m_pUpdates0List,m_pUpdatesNegList。具体逻辑都在CCScheduler中的update()方法中,在后文会具体介绍CCScheduler// touchDispatcherm_pTouchDispatcher CCTouchDispatcher//初始化CCTouchDispatcher,在CCLayer的setToucherEnable()中可以通过CCTouchDispatcher::addStandardDelegate(CCTouchDelegate *pDelegate,int nPriority)或者CCTouchDispatcher::addTargetedDelegate(CCTouchDelegate *pDelegate,int nPriority,bool bSwallowsTouches)方法将CCTouchDelegate注册到CCTouchDispatcherm_pTouchDispatcher// KeypadDispatcherm_pKeypadDispatcher CCKeypadDispatcher//同上,关于实体键// Accelerometerm_pAccelerometer CCAccelerometer//加速度// create autorelease poolpush//CCPoolManager管理多个CCAutoreleasePool,将CCAutoreleasePool放到CCPoolManager中的m_pReleasePoolStacktrue;

    好了,也不讲关于CCDirector的初始化了,还是说说drawScene()吧。

    挺近大别山CCDirecotr的drawScene()方法

  • // calculate "global" dtcalculateDeltaTime//距离上次main loop的时间//tick before glClear: issue #533 m_bPaused//如果暂停 m_pSchedulerupdatem_fDeltaTime//就是刚才在init中所说的CCScheduler的update}/**glClear()函数的作用是用当前缓冲区清除值,也就是glClearColor或者glClearDepth、 glClearIndex、glClearStencil、glClearAccum等函数所指定的值来清除指定的缓冲区,也可以使用glDrawBuffer一次清除多个颜色缓存。比如:glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);第一条语句表示清除颜色设为黑色,第二条语句表示实际完成了把整个窗口清除为黑色的任务,glClear()的唯一参数表示需要被清除的缓冲区**/glClearGL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);m_pNextScene setNextScene//初始化场景相关工作,如onEnter()或onExit()kmGLPushMatrix//矩阵压栈// draw the scenem_pRunningScene m_pRunningScenevisit//遍历正在运行的Scene,先遍历的是CCNode的visit(),因为CCScene没有实现该方法// draw the notifications nodem_pNotificationNode m_pNotificationNode//遍历m_pNotificationNode,可以通过该m_pNotificationNode绘制特殊的节点m_bDisplayStats showStats//左下角的fpskmGLPopMatrix//从矩阵栈中弹出m_uTotalFrames++;// swap buffersm_pobOpenGLView m_pobOpenGLViewswapBuffers calculateMPF}

    OpenGL ES 2.0 已经放弃了固定的渲染流水线,取而代之的是自定义 的各种着色器,在这种情况下变换操作通常需要由开发者来维护。所幸引擎也引入了一套第三方库 Kazmath,它使得我们几 乎可以按照原来 OpenGL ES 1.0 所采用的方式进行开发

    Cocos2d-x2.0中矩阵函数的替代函数

    上诉是关于OpenGL ES 1.0和Kazmath比对图

    跟进到CCNode的visit()方法

  • m_bVisible//如果不可见,则直接返回,不递归迭代return//压入矩阵m_pGrid && m_pGridisActive())//处理CCGridBase的特效beforeDrawthistransform//进行坐标系的变换,没有坐标系的变换,则无法在正确的位置绘制出纹理.变换矩阵等价于坐标系变换.变换矩阵是如何根据当前节点的位置、旋转角度和 缩放比例等属性计算出来的了。形象地讲,transform 方法的任务就是根据当前节点的属性计算出如何把绘图坐标系变换为 新坐标系的矩阵CCNode pNode ;unsigned i ifm_pChildren m_pChildrencount()>//遍历子类 sortAllChildren// draw children zOrder < 0 ccArray arrayData data;先遍历zOrder <的子类for arrayDatanum i++(*)arri pNodem_nZOrder 0break// self drawdraw//绘制自身//遍历剩下的CCNodepNodeelse// 恢复工作m_uOrderOfArrival afterDraw);//出栈

    下面继续跟进到transform()方法

  • kmMat4 transfrom4x4// Convert 3x3 into 4x4 matrixCCAffineTransform tmpAffine nodeToParentTransform//获取节点相对于父节点的变换矩阵CGAffineToGL(&tmpAffine transfrom4x4mat//具体转换transfrom4x414 m_fVertexZ//设置 z 坐标kmGLMultMatrix&transfrom4x4 //右乘一个矩阵transfrom4x4// 处理m_pGrid特效平移相关 m_pCamera != NULL !(bool translate m_obAnchorPointInPointsx 0.0f|| m_obAnchorPointInPointsy kmGLTranslatefRENDER_IN_SUBPIXELx),255)"> RENDER_IN_SUBPIXELy m_pCameralocate(-}
    仿射变换

    一般来说游戏中会大量使用旋转,缩放,平移等仿射变换( 所谓仿射变换是指在线性变换的基础上加上平移,平移不是线性变换)。2D计算机图形学中的仿射变换通常是通过和3x3齐次矩阵相乘来实现的。cocos2d中的仿射变换使用了Quartz 2D中的CGAffineTransform类来表示:

  • CGAffineTransformCGFloat a b c d tx ty};typedefCGAffineTransform;

    CGAffineTransform类表示的齐次矩阵如下,由于变换矩阵最后一列总是[0,0,1],所以被省略了:

    由于cocos2d的绘制使用了OpenglES,因此CGAffineTransform只是用来表示2D仿射变换,最终还是要转化成OpenglES的4x4变换矩阵(Opengl是3D的世界,因此它接受的齐次矩阵是4x4的)。转换工作由CGAffineToGL(const CGAffineTransform *t,GLfloat *m)来完成,Opengl的变换矩阵以一维数组表示,它和CGAffineTransform的映射关系如下:

  • // | m[0] m[4] m[8] m[12] | | m11 m21 m31 m41 | | a c 0 tx |// | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty |// | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 |// | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 |m2 m367891115 ta4c12tx1b5d13ty;

    变换相关的方法:

    "节点坐标系"指的是以一个节点作为参考而产生的坐标系,换句话说,它的任何一个子节点的坐标值都是 由这个坐标系确定的,通过以上方法,我们可以方便地处理触摸点,也可以方便地计算两个不同坐标系下点之间的方向关系。例如,若我们需要判断一个点在另一坐标系下是否在同一个矩形之内,则可以把此点转换为世界坐标系,再从世界坐 标系转换到目标坐标系中,此后只需要通过 contentSize 属性进行判断即可,相关代码如下:

    IsInBoxCCPoint point pointWorld node1convertToWorldSpacepoint pointTarget node2convertToNodeSpacepointWorldCCSize contentSize getContentSize(<= pointTarget contentSizewidthheight

    下面就接着讲draw()方法,在CCNode中,实现方法是空的,我们现在就看下CCSpirit的draw()方法:

  • voidCCSpritevoid CC_PROFILER_START_CATEGORYkCCProfilerCategorySprite "CCSprite - draw"//准备工作,具体啥意思不晓得/** getShaderProgram()->use(); getShaderProgram()->setUniformsForBuiltins(); **/ CC_NODE_DRAW_SETUP//CC_NODE_DRAW_SETUP宏函数用于准备绘制相关环境,设置着色器 ccGLBlendFunc m_sBlendFuncsrcdst //颜色混合函数,与贴图渲染的方式有关 ccGLBindTexture2D m_pobTexturegetName//绑定纹理 enum { kCCVertexAttribFlag_None = 0, kCCVertexAttribFlag_Position = 1 << 0,235)"> kCCVertexAttribFlag_Color = 1 << 1,235)"> kCCVertexAttribFlag_TexCoords = 1 << 2,235)"> kCCVertexAttribFlag_PosColorTex = ( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color | kCCVertexAttribFlag_TexCoords ),235)"> }; ccGLEnableVertexAttribs kCCVertexAttribFlag_PosColorTex //设置OpenGL在后面的渲染中用到顶点位置,定点颜色,纹理坐标属性#define kQuadSize sizeofm_sQuadbl#ifdef EMSCRIPTENlong offset setGLBufferData4 kQuadSize#elselong)&#endif// EMSCRIPTEN// vertex diff offsetof ccV3F_C4B_T2F vertices glVertexAttribPointerkCCVertexAttrib_Position GL_FLOAT GL_FALSEoffset + diff));//顶点坐标// texCoods texCoordskCCVertexAttrib_TexCoords*)(//纹理坐标// color colorskCCVertexAttrib_Color GL_UNSIGNED_BYTE GL_TRUE//顶点颜色 /* BeginMode */ GL_POINTS //把每一个顶点作为一个点进行处理,顶点n即定义了点n,共绘制N个点 GL_LINES //把每一个顶点作为一个独立的线段,顶点2n-1和2n之间共定义了n条线段,总共绘制N/2条线段 GL_LINE_LOOP //绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个顶点定义了线段n,总共绘制n-1条线段 GL_LINE_STRIP //绘制从第一个顶点到最后一个顶点依次相连的一组线段,然后最后一个顶点和第一个顶点相连,第n和n+1个顶点定义了线段n,总共绘制n条线段 GL_TRIANGLES //把每个顶点作为一个独立的三角形,顶点3n-2、3n-1和3n定义了第n个三角形,总共绘制N/3个三角形 GL_TRIANGLE_STRIP //绘制一组相连的三角形,对于奇数n,顶点n、n+1和n+2定义了第n个三角形;对于偶数n,顶点n+1、n和n+2定义了第n个三角形,总共绘制N-2个三角形 GL_TRIANGLE_FAN **/ glDrawArraysGL_TRIANGLE_STRIP//绘制图形 CHECK_GL_ERROR_DEBUG//调试相关处理#if CC_SPRITE_DEBUG_DRAW == 1// draw bounding box]={ ccptlverticesbrtr}; ccDrawPoly#elif CC_SPRITE_DEBUG_DRAW ==2// draw texture box s getTextureRect().size offsetPix getOffsetPositionoffsetPixswidth// CC_SPRITE_DEBUG_DRAW CC_INCREMENT_GL_DRAWS CC_PROFILER_STOP_CATEGORY//结束工作}

    好了,上诉就是关于cocos2d-x整体绘制过程,从mainloop,到visit(),再到draw(),这就是绘制的一条线。

    青春是一场大雨,即使感冒了,还盼回头再淋一次......微笑永远是一个人身上最好看的东西...

    相关文章

        本文实践自 RayWenderlich、Ali Hafizji 的文章《...
    Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
    第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
        Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
    1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
       Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...