前面的渲染流程一文说过,在Node的draw函数中会把渲染命令放到渲染队列,那么如何在队列中的命令具体是如何绘制的,下面分析下2D图形的绘制。调用堆栈大致如下:
由图可见最终调用到的是drawBatchedTriangles函数,在里面会执行OpenGL的绘制操作,代码如下
void Renderer::drawBatchedTriangles()
{
if(_queuedTriangleCommands.empty())
return;
CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_TRIANGLES");
_filledVertex = 0;
_filledIndex = 0;
/************** 1: Setup up vertices/indices *************/
//第一步:设置顶点,索引
_triBatchesToDraw[0].offset = 0;
_triBatchesToDraw[0].indicesToDraw = 0;
_triBatchesToDraw[0].cmd = nullptr;
int batchesTotal = 0;
int prevMaterialID = -1;
bool firstCommand = true;
for(const auto& cmd : _queuedTriangleCommands)
{
auto currentMaterialID = cmd->getMaterialID();
const bool batchable = !cmd->isSkipBatching();
fillVerticesAndIndices(cmd); //将cmd中的顶点和索引存到_verts和_indics中
// in the same batch ?
if (batchable && (prevMaterialID == currentMaterialID || firstCommand))
{
CC_ASSERT((firstCommand || _triBatchesToDraw[batchesTotal].cmd->getMaterialID() == cmd->getMaterialID()) && "argh... error in logic");
_triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount();
_triBatchesToDraw[batchesTotal].cmd = cmd;
}
else
{
// is this the first one?
if (!firstCommand) {
batchesTotal++;
_triBatchesToDraw[batchesTotal].offset = _triBatchesToDraw[batchesTotal-1].offset + _triBatchesToDraw[batchesTotal-1].indicesToDraw;
}
_triBatchesToDraw[batchesTotal].cmd = cmd;
_triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount();
// is this a single batch ? Prevent creating a batch group then
if (!batchable)
currentMaterialID = -1;
}
// capacity full ?
if (batchesTotal + 1 >= _triBatchesToDrawCapacity) {
_triBatchesToDrawCapacity *= 1.4;
_triBatchesToDraw = (TriBatchToDraw*) realloc(_triBatchesToDraw, sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity);
}
prevMaterialID = currentMaterialID;
firstCommand = false;
}
batchesTotal++;
/************** 2: Copy vertices/indices to GL objects *************/
//第2步:拷贝顶点和索引数据到OpenGL对象
auto conf = Configuration::getInstance();
if (conf->supportsShareableVAO() && conf->supportsMapBuffer())
{
//使用VBO
//Bind VAO
GL::bindVAO(_buffersVAO);
//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(_verts[0]) * _filledVertex, _verts, GL_STATIC_DRAW);
// option 3: orphaning + glMapBuffer
// FIXME: in order to work as fast as possible, it must "and the exact same size and usage hints it had before."
// source: https://www.opengl.org/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering
// so most probably we won't have any benefit of using it
//用mapbuffer的方式传递顶点数据,注意用法是,先以nullptr参数调用glBufferData函数,然后调用glMapBuffer返回映射指针buf,然后将数据拷贝到buf,再调用glUnmapBuffer解除mapbuffer(具体可参看[http://www.songho.ca/opengl/gl_vbo.html](http://www.songho.ca/opengl/gl_vbo.html)
)
glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_STATIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(buf, _verts, sizeof(_verts[0]) * _filledVertex);
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); //传递索引数据
}
else
{
// Client Side Arrays
#define kQuadSize sizeof(_verts[0])
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex , _verts, 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]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW);
}
/************** 3: Draw *************/
for (int i=0; i<batchesTotal; ++i)
{
CC_ASSERT(_triBatchesToDraw[i].cmd && "Invalid batch");
_triBatchesToDraw[i].cmd->useMaterial(); //一般激活编译器,绑定纹理等
glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += _triBatchesToDraw[i].indicesToDraw;
}
/************** 4: Cleanup *************/
if (conf->supportsShareableVAO() && conf->supportsMapBuffer())
{
//Unbind VAO
GL::bindVAO(0);
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
_queuedTriangleCommands.clear();
_filledVertex = 0;
_filledIndex = 0;
}
fillVerticesAndIndices函数的代码如下:
void Renderer::fillVerticesAndIndices(const TrianglesCommand* cmd)
{
//拷贝顶点数据到_verts
memcpy(&_verts[_filledVertex], cmd->getVertices(), sizeof(V3F_C4B_T2F) * cmd->getVertexCount());
// fill vertex, and convert them to world coordinates
const Mat4& modelView = cmd->getModelView();
for(ssize_t i=0; i < cmd->getVertexCount(); ++i) //模型视图矩阵,对顶点进行变换
{
modelView.transformPoint(&(_verts[i + _filledVertex].vertices)); //为什么不在GPU做转换
}
// fill index
const unsigned short* indices = cmd->getIndices();
//拷贝索引数据
for(ssize_t i=0; i< cmd->getIndexCount(); ++i)
{
_indices[_filledIndex + i] = _filledVertex + indices[i];
}
_filledVertex += cmd->getVertexCount();
_filledIndex += cmd->getIndexCount();
}
从代码可以看到cocos2d是用VBO和mapbuffer的方式传递顶点和索引,具体用法可以看http://www.songho.ca/opengl/gl_vbo.html
或其他文档说明。
再看下_triBatchesToDraw[i].cmd->useMaterial()函数的内容,如下:
void TrianglesCommand::useMaterial() const
{
//Set texture
GL::bindTexture2D(_textureID); //内部调用activeTexture和glBindTexture
if (_alphaTextureID > 0)
{ // ANDROID ETC1 ALPHA supports.
GL::bindTexture2DN(1, _alphaTextureID);
}
//set blend mode
GL::blendFunc(_blendType.src, _blendType.dst);
_glProgramState->apply(_mv); //一般激活编译器,绑定纹理,更新uniform和attribute等
}
网友评论