cocos的3D部分是在2D部分的基础上加上去,虽然经历很多版本迭代,然而3D部分,只是一个简单架子搭了起来,如果想要譬如高光,阴影,批次渲染等功能,都是需要自己添加的。这里简单讲下3D渲染命令MeshCommand,MeshCommand主要用于渲染Sprite3D 和3D粒子,这里以Sprite3D 的绘制流程为例,记录MeshCommand的渲染流程。
引擎渲染3D场景时,首先遍历场景节点信息,最终调用每个sprite3D节点的draw函数(流程见cocos启动流程),然后遍历调用3D物体的Mesh,生成MeshCommand渲染命令,加入到渲染器Renderer(游戏启动后,只有一个,由Director进行维护)的渲染队列:
void Sprite3D::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
const auto& scene = Director::getInstance()->getRunningScene();
if (usingLight != _shaderUsingLight)
{
genMaterial(usingLight);//如果材质不存在,生成新材质
}
for (auto mesh: _meshes)
{
//调用mesh的draw生成渲染命令
mesh->draw(.......);
}
}
Sprite3D与2D物体的差别在于,渲染更加多样,一个物体有多个部位,每个部位的渲染信息也各不相同(然而cocos是相同的,一个物体只有一个材质,后面项目已经改成多材质),因此需要一个物体分成多个MESH进行渲染,并且需要为每个mesh的材质传入自己uniform变量,Mesh的draw函数如下:
void Mesh::draw(Renderer* renderer.......)
{
//生成渲染指令
_meshCommand.init(........);
//每个mesh有多个通道,为每个通道传入shader的uniform值(光照信息,朝向信息等)
for(const auto pass : technique->_passes)
{
auto programState = pass->getGLProgramState();
programState->setUniformVec4("u_color", color);
if (scene && scene->getLights().size() > 0 && pass->ifAcceptLight())
setLightUniforms(pass, scene, color, lightMask);
}
//添加到渲染队列
renderer->addCommand(&_meshCommand);
}
遍历完成后,渲染器访问渲染队列,根据渲染类型(global<0,3D不透明,3D透明,2D,global>0)遍历执行渲染命令,处理MeshCommand的命令如下:
void Renderer::processRenderCommand(RenderCommand* command)
{
auto commandType = command->getType();
if (RenderCommand::Type::MESH_COMMAND == commandType)
{
auto cmd = static_cast<MeshCommand*>(command);
if (.......)
{
flush3D();
if(cmd->isSkipBatching())//用于判断是否批次,实际只是判断是否透明,透明物体以及粒子
cmd->execute();
else
{
cmd->preBatchDraw();
cmd->batchDraw();
_lastBatchedMeshCommand = cmd;
}
}
else
cmd->batchDraw();//不会执行,设计未完成,虽然写着批次渲染,然而并未实现
}
}
MeshCommand虽然写了批次渲染的接口,只是挂羊头买狗肉,最终都是单个渲染,batchDraw和execute,两边并无区别,代码如下:
void MeshCommand::batchDraw()
{
if (_material)
{
for(const auto& pass: _passes)
{
//设置渲染状态以及Uniform,顶点属性变量
pass->bind(_mv);
glDrawElements(_primitive, (GLsizei)_indexCount, _indexFormat, 0);
pass->unbind();
}
}
else
{
//设置渲染状态以及Uniform,顶点属性变量
_glProgramState->applyGLProgram(_mv);
// set render state
applyRenderState();
// Draw
glDrawElements(_primitive, (GLsizei)_indexCount, _indexFormat, 0);
}
}
如果要实现3D的批次渲染,需要使用CutomMeshCommand,自己进行批次渲染的合并,3D批次渲染代码太多就不贴了。