auto culling
自动剪裁,也就是把超出屏幕的quad自动移出渲染队列。我们的渲染关键是两个,一个是quad队列,一个是cmd队列。
void Renderer::renderBatch()
{
_drawnBatches = _drawnVertices = 0;
if(_numQuads <= 0 || _batchedQuadCommands.empty())
{
return;
}
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _numQuads , _quads, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(_vertexPosition);
glVertexAttribPointer(
_vertexPosition, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _position)) // array buffer offset
);
glEnableVertexAttribArray(_vertexUV);
glVertexAttribPointer(
_vertexUV, // attribute 0. No particular reason for 0, but must match the layout in the shader.
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _texCoord)) // array buffer offset
);
glEnableVertexAttribArray(_vertexColor);
glVertexAttribPointer(
_vertexColor, // attribute 0. No particular reason for 0, but must match the layout in the shader.
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
kQuadSize, // stride
(void*)(offsetof( Vertex, _color)) // array buffer offset
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
glActiveTexture(GL_TEXTURE0);
int quadsToDraw = 0;
int startQuad = 0;
GLuint _lastTextureID = 0;
//Start drawing verties in batch
for(const auto& cmd : _batchedQuadCommands)
{
auto newTextureID = cmd->getTextureID();
if(_lastTextureID != newTextureID )
{
//Draw quads
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;
}
//Use new Texture
cmd->useTexture();
//优化后直接给单位矩阵
//caculateMVP(cmd->getModel());
caculateMVP(Mat4(1.0));
_lastTextureID = newTextureID;
}
quadsToDraw += cmd->getQuadCount();
}
if(quadsToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) quadsToDraw*6, GL_UNSIGNED_SHORT, (GLvoid*) (startQuad*6*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += quadsToDraw*6;
}
for(auto& cmd : _batchedQuadCommands)
{
delete cmd;
}
_batchedQuadCommands.clear();
_numQuads = 0;
}
void Renderer::visit()
{
int queueSize = _renderQueue.size();
float perZorder = (zFar - zNear)/(float)(queueSize+1) ;
//优化排序
for (int i = 0; i < queueSize; ++i)
{
QuadCommand* cmd = _renderQueue[i];
Quad* quad = cmd->getQuads();
int quadCount = cmd->getQuadCount();
float zOrder = perZorder * (queueSize - i);
for (int j = 0; j < quadCount; ++j)
{
quad[j].tl._position.z = zOrder;
quad[j].bl._position.z = zOrder;
quad[j].tr._position.z = zOrder;
quad[j].br._position.z = zOrder;
}
}
std::sort( std::begin(_renderQueue), std::end(_renderQueue),quadComparisonLess);
//复制到批渲染队列
copyToBatchRender(queueSize);
//剪裁超出屏幕的quad
cullQuadForBatchRender();
}
void Renderer::copyToBatchRender( int queueSize )
{
for (int i = 0; i < queueSize; ++i)
{
QuadCommand* cmd = _renderQueue[i];
memcpy(_preCullQuads + _preNumQuads, cmd->getQuads(), sizeof(Quad) * cmd->getQuadCount());
convertToWorldCoordinates(_preCullQuads + _preNumQuads, cmd->getQuadCount(), cmd->getModel());
BatchCommand* batchCommand = new BatchCommand();
memcpy(batchCommand,(BatchCommand*)cmd,sizeof(BatchCommand));
_batchedQuadCommands.push_back(batchCommand);
_preNumQuads += cmd->getQuadCount();
}
_renderQueue.clear();
}
我们把渲染quad复制到一个临时块中,然后检测后复制到渲染队列中。
void Renderer::cullQuadForBatchRender()
{
if(_preNumQuads <= 0 || _batchedQuadCommands.empty())
{
return;
}
int quadIndex = 0;
int startQuadNumber = 0;
int cullQuadNumber = 0;
Size winSize = Engine::getInstance()->getDesignSize();
Rect rectWindow(-winSize.width/2 ,-winSize.height/2 ,winSize.width,winSize.height);
std::vector<BatchCommand*>::iterator cmd = _batchedQuadCommands.begin();
while (quadIndex < _preNumQuads)
{
Quad *q = &_preCullQuads[quadIndex];
Rect rectQuad(q->bl._position.x,q->bl._position.y,q->br._position.x - q->bl._position.x, q->tl._position.y - q->bl._position.y);
if(rectWindow.intersectsRect(rectQuad))
{
memcpy(_quads+_numQuads,q,sizeof(Quad));
}
else
{
++cullQuadNumber;
}
++startQuadNumber;
if(startQuadNumber == (*cmd)->getQuadCount())
{
int quadCount = startQuadNumber - cullQuadNumber;
if (quadCount < 1)
{
cmd = _batchedQuadCommands.erase(cmd);
}
else
{
(*cmd)->setQuadCount(quadCount);
++cmd;
}
_numQuads -= cullQuadNumber;
startQuadNumber = 0;
cullQuadNumber = 0;
}
++quadIndex;
++_numQuads;
}
_preNumQuads = 0;
}