cocos2d-x 3.x渲染流程

撰写于 2018-01-22 修改于 2018-01-29 分类 游戏开发 标签 cocos2d-x

虽然学习了opengl,但是没怎么实践,就先看一下cocoos2d-x是怎么处理的,学习一下,不看不知道,一看吓一跳,代码的拼凑感太强了,完全不像是一个完整的系统,可能是后期很多人修改的缘故吧,不纠结了,大致学习一下就行。

入口

win32入口当然是从main函数开始,cocoos2d-x的每个平台的入口文件都不一样,cocoos2d-x把这些差异性的文件都提取了出来,单独处理,这些入口源码不是研究重点,也没什么可研究,大致提一下。

  1. 初始化窗口参数
  2. 创建并设置窗口
  3. 初始化lua引擎状态

窗口系统

没什么意外,win32嘛,窗口系统依旧使用的glfw,其他的也想不到用什么了,开源,好用,而且还在更新,不再赘述,附上官网glfw官网

渲染部分

渲染入口

win32的渲染入口在Application:run()函数里面,下面把核心部分拿出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

int Application::run()
{
/*
.
.
.
*/
/*此处为一个死循环,windowShouldClose()为true时退出,由glfwSetWindowShouldClose控制,当点击窗口的关闭按钮时会设置为true*/
while(!glview->windowShouldClose())
{
/*获取精确的时间(从机器开机到现在的时间)*/
QueryPerformanceCounter(&nNow);
/*如果上次渲染的时间和当前时间差 大于 设置的帧率 (一般是1/60s)则开始渲染,保证帧率*/
if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
{
nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);

director->mainLoop();
glview->pollEvents();
}
/*否则暂停1ms*/
else
{
Sleep(1);
}
}
/*
.
.
.
*/
return true;
}

主循环

如果满足了帧率,则开始进行循环代码DisplayLinkDirector::mainLoop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void DisplayLinkDirector::mainLoop()
{
/*如果清理Director(只有调用了 director->end() 后,_purgeDirectorInNextLoop置为true)*/
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
/*如果 _invalid 为false,则开始渲染(当调用 director->stopAnimation(),设置_invalid为true,则不进行渲染)*/
else if (! _invalid)
{
/*渲染场景*/
drawScene();

/*清理自动回收池(cocos2d-x的内存管理机制,稍后会进行说明)*/
PoolManager::getInstance()->getCurrentPool()->clear();
}
}

渲染场景

cocos2d-x就是通过每一帧drawScene进行渲染场景。这里面用到了Render类,自从cocos2d-x 3.0以后,渲染部分拿了出来,组成了单独一个类。以前的都是放在了每个可渲染节点的draw函数里面,draw函数可以看到渲染代码。而现在的draw函数里面只是生成了一个渲染命令,然后加入渲染队列,交由Render类进行具体渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
void Director::drawScene()
{
/*计算出两次渲染 调用的时间 放入_delaTime*/
calculateDeltaTime();

/*当两次渲染的间隔时间过小,基本等于0,则不再进行渲染,直接返回*/
if(_deltaTime < FLT_EPSILON)
{
return;
}
/*无所谓的函数,空函数,不用处理*/
if (_openGLView)
{
_openGLView->pollEvents();
}

if (! _paused)
{
/*调用_scheduler的update函数*/
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
/*清理OpenGL颜色缓冲区和深度缓冲区*/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/*切换场景。为了防止闪烁,切换场景一定要在未渲染之前进行。*/
if (_nextScene)
{
setNextScene();
}
/*设置opengl当前矩阵为 modelview 矩阵 */
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

if (_runningScene)
{
/*clear draw stats*/
_renderer->clearDrawStats();

/*渲染当前场景*/
_runningScene->render(_renderer);

_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
/*绘制观察者节点*/
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}

if (_displayStats)
{
showStats();
}
_renderer->render();

_eventDispatcher->dispatchEvent(_eventAfterDraw);

popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

_totalFrames++;

if (_openGLView)
{
/*交换双缓冲区*/
_openGLView->swapBuffers();
}

if (_displayStats)
{
calculateMPF();
}
}

上面的代码,核心的代码为runningScene->render(renderer), 即调用场景的render函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void Scene::render(Renderer* renderer)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
const auto& transform = getNodeToParentTransform();
/*是否有自定义的其他camera,如果有,则分别处理不同camera下的节点*/
for (const auto& camera : _cameras)
{
Camera::_visitingCamera = camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
defaultCamera = Camera::_visitingCamera;
continue;
}

director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());

/*visit the scene*/
visit(renderer, transform, 0);
renderer->render();

director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
/*如果没有自定义的camera,则直接遍历,渲染*/
if (defaultCamera)
{
Camera::_visitingCamera = defaultCamera;
/*加载透视矩阵*/
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());

/*遍历场景*/
visit(renderer, transform, 0);
/*渲染*/
renderer->render();

director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
Camera::_visitingCamera = nullptr;
}

下面提一下visit函数,renderer->render()不在这里说明,下一篇会具体解读,visit函数会遍历每个节点下面的子节点,并调用节点自身的draw函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
/*不可见直接返回*/
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);
/*在当前相机下,该node是否可见,只有node的flag同相机的掩码相同才会可见*/
bool visibleByCamera = isVisitableByVisitingCamera();

int i = 0;

if(!_children.empty())
{
/*按localzorder从小到大排序,如果localzorder相同,则按照添加的先后顺序*/
sortAllChildren();
/*先遍历localzorder小于0的节点,先把这些节点加入到渲染队列*/
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);

if ( node && node->_localZOrder < 0 )
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
/*再渲染自己*/
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
/*再遍历其他普通的节点(即localzorder大于等于0的)*/
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
/*没有子节点,则直接渲染,但draw函数里面并没有进行渲染,而是生成了一条渲染命令,加入到渲染队列中。*/
this->draw(renderer, _modelViewTransform, flags);
}

director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}

draw函数里面并没有进行渲染,而是生成了一条渲染命令,加入到渲染队列中,下面以sprite::draw()为例,进行说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
/***此处用于判断 node 通过 transform 变化后,是否已经出屏幕了,也就是检测是否被裁剪***/
_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);
}
}

以上即为cocos2d-x 3.x的整个渲染流程,但其中并没有提及如何渲染,即Render的相关功能,会在其后的文章中进行说明。

Site by ZHJ using Hexo & Random

Hide