您的位置:首页 > 移动开发 > Cocos引擎

【cocos2d-x 源码解析】创建 OpenGL 环境

2017-10-27 15:12 441 查看

创建 OpenGL 上下文

cocos2d-x 是基本 OpenGL 实现的,所以游戏启动之后做的第一件事就是创建 OpenGL 上下文,之后才能进行渲染。在 win32 上,创建 OpenGL context 的过程就是创建一个 OpenGL 窗口,cocos2d-x 使用的是 glfw 库,首先我们回顾一下 glfw 创建窗口的过程

int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(400, 400, "Color", nullptr, nullptr);
if (window == nullptr) { return 1; }
glfwMakeContextCurrent(window);

glewExperimental = GL_TRUE;
if (GLEW_OK != glewInit()) { return 2; }

glClearColor(255, 255, 255, 255);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
//render
glfwSwapBuffers(window);
}

glfwTerminate();
return 0;
}


这是 glfw 创建 OpenGL 窗口、调用 OpenGL 渲染指令以及监听处理各种事件的过程,接下来看看 cocos2d-x 中是如何使用 glfw 的。

回顾窗口启动

关于 cocos2d-x 的窗口启动过程可以看我前面的文章

cocos2d-x 窗口启动

下面简单的回顾一下这个过程。在 main 函数创建一个 AppDelegate 实例之后,调用 Application 的 run 方法启动游戏

int Application::run()
{
//...
initGLContextAttrs();

if (!applicationDidFinishLaunching())
{
return 1;
}

auto director = Director::getInstance();
auto glview = director->getOpenGLView();
glview->retain();

while(!glview->windowShouldClose())
{
//...
director->mainLoop();
glview->pollEvents();
}

if (glview->isOpenGLReady())
{
director->end();
director->mainLoop();
director = nullptr;
}
glview->release();
return 0;
}


这是 run 方法的关键代码,首先调用 initGLContextAttrs 设置 OpenGL context 的属性,这个方法在其子类 Appdelegate 中实现

void AppDelegate::initGLContextAttrs()
{
GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};
GLView::setGLContextAttrs(glContextAttrs);
}


目前 cocos2d-x 只能设置六个属性,分别是 rgba 四个颜色属性,深度和模板;定义一个 GLContextAttrs 变量,然后调用 GLView 的静态方法进行设置,GLView 是我们这篇文章的重点,下面会解析这个类。设置完 OpenGL Context 属性之后,调用 applicationDidFinishLaunching 方法进行一些初始化,这个方法也是在子类 AppDelegate 中实现

bool AppDelegate::applicationDidFinishLaunching() {
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
glview = GLViewImpl::createWithRect("CppSource", Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
glview = GLViewImpl::create("CppSource");
#endif
director->setOpenGLView(glview);
}
//...
return true;
}


applicationDidFinishLaunching 做了很多初始化工作,其中最重要的就是初始化导演实例 Director 和 OpenGL 窗口管理类实例 GLView。Director 对象中有一个 GLView 实例,该实例默认为空,调用 GLViewImpl 的 createWithRect 创建一个实例。GLViewImpl 继承自 GLView,是桌面程序的 OpenGL 窗口管理类,而 GLView 则是一个上层的 OpenGL 窗口管理类,提供部分接口,更多的接口则在 GLViewImpl 中实现。GLView 和 GLViewImpl 的目录结构

|-cocos
|-platform
|-CCGLView.h
|-CCGLView.cpp
|-desktop
|-CCGLViewImpl-desktop.h
|-CCGLViewImpl-desktop.cpp


applicationDidFinishLaunching 执行完成之后就得到了一个 Director 实例和一个 GLViewImpl 实例,OpenGL 窗口也创建出来了,OpenGL context 也存在了,可以说 OpenGL 环境已经具备了,接下来就可以开始渲染了。在 while 循环中调用 GLView 的 windowShouldClose 检测 OpenGL 窗口是否关闭,如果关闭了则跳出循环,做一些回收操作然后程序结束,如果没关闭则程序在这里阻塞,每一帧都会进入到这个 while 循环体中,这个循环体做的事情就是监听事件和 OpenGL 渲染。

GLView(GLViewImpl)

接下来就是主角 GLView(GLViewImpl) 登场了,首先看 GLViewImpl 的构造函数

GLViewImpl::GLViewImpl(bool initglfw)
: _captured(false)
, _supportTouch(false)
, _isInRetinaMonitor(false)
, _isRetinaEnabled(false)
, _retinaFactor(1)
, _frameZoomFactor(1.0f)
, _mainWindow(nullptr)
, _monitor(nullptr)
, _mouseX(0.0f)
, _mouseY(0.0f)
{
_viewName = "cocos2dx";
g_keyCodeMap.clear();
for (auto& item : g_keyCodeStructArray)
{
g_keyCodeMap[item.glfwKeyCode] = item.keyCode;
}

GLFWEventHandler::setGLViewImpl(this);
if (initglfw)
{

4000
glfwSetErrorCallback(GLFWEventHandler::onGLFWError);
glfwInit();
}
}


这里做了两件重要的事,第一件就是初始化 GLFWEventHandler;第二件就是执行
glfwInit()
,也就是我们熟悉的 glfw 初始化了。接下来看GLFWEventHandler 这个类,这个类在 CCGLViewImpl-desktop.cpp 中定义,它的内容很简单

class GLFWEventHandler
{
public:
static void onGLFWError(int errorID, const char* errorDesc)
{
if (_view)
_view->onGLFWError(errorID, errorDesc);
}

static void onGLFWMouseCallBack(GLFWwindow* window, int button, int action, int modify)
{
if (_view)
_view->onGLFWMouseCallBack(window, button, action, modify);
}

static void onGLFWMouseMoveCallBack(GLFWwindow* window, double x, double y)
{
if (_view)
_view->onGLFWMouseMoveCallBack(window, x, y);
}

static void onGLFWMouseScrollCallback(GLFWwindow* window, double x, double y)
{
if (_view)
_view->onGLFWMouseScrollCallback(window, x, y);
}

static void onGLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (_view)
_view->onGLFWKeyCallback(window, key, scancode, action, mods);
}

static void onGLFWCharCallback(GLFWwindow* window, unsigned int character)
{
if (_view)
_view->onGLFWCharCallback(window, character);
}

static void onGLFWWindowPosCallback(GLFWwindow* windows, int x, int y)
{
if (_view)
_view->onGLFWWindowPosCallback(windows, x, y);
}

static void onGLFWframebuffersize(GLFWwindow* window, int w, int h)
{
if (_view)
_view->onGLFWframebuffersize(window, w, h);
}

static void onGLFWWindowSizeFunCallback(GLFWwindow *window, int width, int height)
{
if (_view)
_view->onGLFWWindowSizeFunCallback(window, width, height);
}

static void setGLViewImpl(GLViewImpl* view)
{
_view = view;
}

static void onGLFWWindowIconifyCallback(GLFWwindow* window, int iconified)
{
if (_view)
{
_view->onGLFWWindowIconifyCallback(window, iconified);
}
}

private:
static GLViewImpl* _view;
};


可以看到,这是一个只有静态方法的工具类,用于 glfw 的事件回调处理。首先调用 setGLViewImpl 保存一个 GLViewImpl 实例,然后注册 glfw 事件时直接使用这个类的静态方法作为回调函数,当事件触发时就会调用这个类的静态方法,然后再调用 _view 相应的方法,这样就达到了将 GLViewImpl 的方法注册给事件的目的,所以 GLFWEventHandle 其实是一个中介,将 glfw 事件回调和 GLViewImpl 的实例方法联系起来。

总结起来,GLViewImpl 的构造函数做了这三件事件,初始化 GLFWEventHandler,注册一个错误回调函数,初始化 glfw。接下来回到 createWithRect 方法

GLViewImpl* GLViewImpl::createWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
auto ret = new (std::nothrow) GLViewImpl;
if(ret && ret->initWithRect(viewName, rect, frameZoomFactor)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}


这个方法也很简单,new 一个 GLViewImpl 实例,然后调用 initWithRect 方法

bool GLViewImpl::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
setViewName(viewName);

_frameZoomFactor = frameZoomFactor;

glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
glfwWindowHint(GLFW_RED_BITS,_glContextAttrs.redBits);
glfwWindowHint(GLFW_GREEN_BITS,_glContextAttrs.greenBits);
glfwWindowHint(GLFW_BLUE_BITS,_glContextAttrs.blueBits);
glfwWindowHint(GLFW_ALPHA_BITS,_glContextAttrs.alphaBits);
glfwWindowHint(GLFW_DEPTH_BITS,_glContextAttrs.depthBits);
glfwWindowHint(GLFW_STENCIL_BITS,_glContextAttrs.stencilBits);

int needWidth = rect.size.width * _frameZoomFactor;
int neeHeight = rect.size.height * _frameZoomFactor;

_mainWindow = glfwCreateWindow(needWidth, neeHeight, _viewName.c_str(), _monitor, nullptr);

//...

int realW = 0, realH = 0;
glfwGetWindowSize(_mainWindow, &realW, &realH);

//...

glfwMakeContextCurrent(_mainWindow);

glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack);
glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack);
glfwSetScrollCallback(_mainWindow, GLFWEventHandler::onGLFWMouseScrollCallback);
glfwSetCharCallback(_mainWindow, GLFWEventHandler::onGLFWCharCallback);
glfwSetKeyCallback(_mainWindow, GLFWEventHandler::onGLFWKeyCallback);
glfwSetWindowPosCallback(_mainWindow, GLFWEventHandler::onGLFWWindowPosCallback);
glfwSetFramebufferSizeCallback(_mainWindow, GLFWEventHandler::onGLFWframebuffersize);
glfwSetWindowSizeCallback(_mainWindow, GLFWEventHandler::onGLFWWindowSizeFunCallback);
glfwSetWindowIconifyCallback(_mainWindow, GLFWEventHandler::onGLFWWindowIconifyCallback);

setFrameSize(rect.size.width, rect.size.height);

//...

initGlew();

glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
return true;
}


在这个方法里看到很多熟悉的 glfw 函数,首先调用 glfwWindowHint 设置一些参数,然后调用 glfwCreateWindow 创建窗口,调用 glfwMakeContextCurrent 将创建出来的窗口设置为 OpenGL 的当前上下文;之后是注册各种事件,可以看到事件的回调函数都是 GLFWEventHandler 的静态方法,最后调用 initGlew 初始化 glew

bool GLViewImpl::initGlew()
{
#if (CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
MessageBox((char *)glewGetErrorString(GlewInitResult), "OpenGL error");
return false;
}
//...

return true;
}


initGlew 只是简单地调用 glewInit 函数,然后加一些成功与否判断而已。GLViewImpl 的构造函数和 initWithRect 方法,完成了 glfw 和 glew 的初始化工作,创建一个 OpenGL 窗口并设置 OpenGL context,注册各种 glfw 事件,所以我们可以说执行完 AppDelegate 的 applicationDidFinishLuanching 方法之后,OpenGL 环境已全部初始完成。

接下来回到 Application 的 run 方法,调用 applicationDidFinishLuanching 方法初始化 OpenGL 环境之后,在 while 循环条件体不断调用 GLView 的 windowShouldClose 方法检测窗口是否关闭

bool GLViewImpl::windowShouldClose()
{
if(_mainWindow)
return glfwWindowShouldClose(_mainWindow) ? true : false;
else
return true;
}


就是简单地调用 glfwWindowShouldClose 函数而已。如果窗口没有关闭,则循环执行两件事,监听事件和渲染,监听事件也很简单

void GLViewImpl::pollEvents()
{
glfwPollEvents();
}


而渲染则是调用 Director 的 mainLoop 方法

void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();

// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}


Director 中有两个控制变量,_purgeDirectorInNextLoop 为真时清除导演,_restartDirectorInNextLoop 为真时重置导演,两个变量都为假时才调用 drawScene,很明显 drawScene 做的事就是真正的渲染场景了

void Director::drawScene()
{
// calculate "global" dt
calculateDeltaTime();

if (_openGLView)
{
_openGLView->pollEvents();
}

//tick before glClear: issue #533
if (! _paused)
{
_eventDispatcher->dispatchEvent(_eventBeforeUpdate);
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}

_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();
/* to avoid flickr, nextScene MUST be here: after tick and before draw.
* FIXME: 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);

if (_runningScene)
{
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
_runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
//clear draw stats
_renderer->clearDrawStats();

//render the scene
_runningScene->render(_renderer);

_eventDispatcher->dispatchEvent(_eventAfterVisit);
}

// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}

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();
}
}


drawScene 做的事比较,大概可分为三部分,处理计时和事件,切换场景,绘制场景。在第一部分中,首先调用 calculateDeltaTime 计算帧间隔时间,然后调用 glfwPollEvents 监听事件,最后执行调度器 scheduler 的 update 方法。第二部分只做一件事,检测是否需要切换到下个场景,如果需要则调用 setNextScene 方法设置 _runningScene 的值为 _nextScene。第三部分开始真正绘制场景,在导演 Director 中有两个变量,_runningScene 和 _render,分别表示当前运行的场景和渲染器。场景渲染的流程如下

* 第一步,如果使用了物理引擎或者 3D 导航 api,则调用 setPhysicsAndNavigation 方法执行相应的 update 方法

* 第二步,调用渲染器的 clearDrawStats 方法清除绘制统计

* 第三步,调用场景的 render 方法绘制场景中的所有对象

* 第四步,如果 _displayStats 为真则绘制调试信息

* 第五步,调用 glfwSwapBuffers 完成执行 OpenGL 渲染

其中最重要的是第三步,即场景的 render 方法

void Scene::render(Renderer* renderer)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
const auto& transform = getNodeToParentTransform();

for (const auto& camera : getCameras())
{
if (!camera->isVisible())
continue;

Camera::_visitingCamera = camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
defaultCamera = Camera::_visitingCamera;
}

director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
camera->apply();
//clear background with max depth
camera->clearBackground();
//visit the scene
visit(renderer, transform, 0);
#if CC_USE_NAVMESH
if (_navMesh && _navMeshDebugCamera == camera)
{
_navMesh->debugDraw(renderer);
}
#endif

renderer->render();

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

#if CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION
if (_physics3DWorld && _physics3DWorld->isDebugDrawEnabled())
{
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, _physics3dDebugCamera != nullptr ? _physics3dDebugCamera->getViewProjectionMatrix() : defaultCamera->getViewProjectionMatrix());
_physics3DWorld->debugDraw(renderer);
renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
#endif

Camera::_visitingCamera = nullptr;
experimental::FrameBuffer::applyDefaultFBO();
}


首先取到场景的摄像机 Camera,然后调用 Camera.apply 方法填充 FBO 对象和设置视口

void Camera::apply()
{
applyFrameBufferObject();
applyViewport();
}

void Camera::applyViewport()
{
if(nullptr == _fbo)
{
glViewport(getDefaultViewport()._left, getDefaultViewport()._bottom, getDefaultViewport()._width, getDefaultViewport()._height);
}
else
{
glViewport(_viewport._left * _fbo->getWidth(), _viewport._bottom * _fbo->getHeight(),
_viewport._width * _fbo->getWidth(), _viewport._height * _fbo->getHeight());
}

}


然后调用 Camera.clearBackground 方法绘制背景

void Camera::clearBackground()
{
if (_clearBrush)
{
_clearBrush->drawBackground(this);
}
}

void CameraBackgroundDepthBrush::drawBackground(Camera* camera)
{
//...

_glProgramState->setUniformFloat("depth", _depth);
_glProgramState->apply(Mat4::IDENTITY);
GLshort indices[6] = {0, 1, 2, 3, 2, 1};

{
GL::bindVAO(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);

// vertices
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), &_quad.tl.vertices);

// colors
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_T2F), &_quad.tl.colors);

// tex coords
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), &_quad.tl.texCoords);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
//..
}


在这个方法里我们终于看到了一些 OpenGL 的指令,比如 bindVAO 绑定顶点数组对象,glBindBuffer 绑定索引缓冲对象,glVertexAttribPointer 链接顶点属性,glDrawElements 使用索引绘制。接下来是调用 visit 方法,这个方法在基类 Node 中定义

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->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);

bool visibleByCamera = isVisitableByVisitingCamera();

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
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);

for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
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;
}


这个方法很长,它的作用就是递归地调用每一个子结点,然后执行每个结点的 draw 方法,每种需要绘制的结点都实现了 draw 方法,我们以 Sprite 为例

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
//...
{
_trianglesCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
renderer->addCommand(&_trianglesCommand);
//...
}
}


其核心代码就是创建一个渲染指令,然后将指令添加到渲染器 renderer 中去。接下来就是调用渲染器的 render 方法

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)
{
//Process render commands
//1. Sort render commands based on ID
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort();
}
visitRenderQueue(_renderGroups[0]);
}
clean();
_isRendering = false;
}


这个方法先对所有渲染组进行排列,然后渲染第一个组,调用 visitRenderQueue 进行渲染

void Renderer::visitRenderQueue(RenderQueue& queue)
{
queue.saveRenderState();

const auto& zNegQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);
if (zNegQueue.size() > 0)
{
//...
for (auto it = zNegQueue.cbegin(); it != zNegQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
//...
}


这又是一个特别特别长的方法,其核心代码就是遍历渲染指令,然后调用 processRenerCommand 执行该指令

void Renderer::processRenderCommand(RenderCommand* command)
{
auto commandType = command->getType();
if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
{
//Draw if we have batched other commands which are not triangle command
flush3D();
flushQuads();

//Process triangle command
auto cmd = static_cast<TrianglesCommand*>(command);

//Draw batched Triangles if necessary
if(cmd->isSkipBatching() || _filledVertex + cmd->getVertexCount() > VBO_SIZE || _filledIndex + cmd->getIndexCount() > INDEX_VBO_SIZE)
{
CCASSERT(cmd->getVertexCount()>= 0 && cmd->getVertexCount() < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
CCASSERT(cmd->getIndexCount()>= 0 && cmd->getIndexCount() < INDEX_VBO_SIZE, "VBO for index is not big enough, please break the data down or use customized render command");
//Draw batched Triangles if VBO is full
drawBatchedTriangles();
}

//Batch Triangles
_batchedCommands.push_back(cmd);

fillVerticesAndIndices(cmd);

if(cmd->isSkipBatching())
{
drawBatchedTriangles();
}

}
//else if(...)
}


在 processRenderCommand 方法中就可以看到很多绘制方法了,我们随便打开一个诸如 drawBatchedTriangles 的方法,可以看到全是 OpenGL 指令

总结

在 Application 的 run 方法中调用 AppDelegate 的 applicationDidFinishLaunching 方法,在这个方法中创建并初始化 GLView,这个过程就是创建和初始化 OpenGL 的过程,经过这过程之后,一个 OpenGL 窗口就被创建出来了

在 Application 的 run 方法中调用 GLView 的 windowShouldClose 方法检测窗口是否关闭,如果未关闭则都调用渲染指令

Driector 的 drawScene 负责绘制渲染,首先拿到当前场景的摄像机并设置视口,然后递归遍历所有结点并调用结点的 draw 方法

结点的 draw 方法创建一个渲染 command 并把这个 command 添加到渲染器 renderer 中去

在 Driector 的 drawScene 方法中调用渲染器的 render 方法进行渲染,render 方法取到渲染指令后调用 processRenderCommand 执行 OpenGL 指令
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息