[√]cocos2dx particleSystem渲染过程解读、优化的思考

简介: [√]cocos2dx particleSystem渲染过程解读、优化的思考

Particle Use Case

创建粒子并加入场景的demo示例:

auto particle = ParticleSystemQuad::create("dc/atom.plist");
particle->setPosition(size.width / 2, size.height / 2);
this->addChild(particle);

Particle源码解析

顺着上边的代码,我们最直接的就会找到

bool ParticleSystem::initWithFile(const std::string& plistFile){
    // 从文件中读取字典数据,并进行初始化
    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(_plistFile);
    ret = this->initWithDictionary(dict, listFilePath);
}  
bool ParticleSystem::initWithDictionary(ValueMap& dictionary, const std::string& dirname){
    int maxParticles = dictionary["maxParticles"].asInt();
    if(this->initWithTotalParticles(maxParticles)){
    }
}
bool ParticleSystem::initWithTotalParticles(int numberOfParticles)
{
    // 这里初始化_particleData
    if( !_particleData.init(_totalParticles) )
    if (_batchNode)// 批处理节点,类似SpriteBatchNode
    {
        for (int i = 0; i < _totalParticles; i++)
        {
            _particleData.atlasIndex[i] = i;
        }
    }
}   
bool ParticleData::init(int count)
{
    maxCount = count;
    // 申请了最大粒子数量的内存空间,用来存放每个粒子的数据,通过offset来确定粒子的具体数据
    posx= (float*)malloc(count * sizeof(float));
    posy= (float*)malloc(count * sizeof(float));
    // ...
}
particle变量 plist key 解释
_totalParticles maxParticles 粒子的最大数量
_life particleLifespan 粒子的生命周期时间

剩下的逻辑就是靠update驱动

void ParticleSystem::update(float dt)
{
    if (_isActive && _emissionRate && _AllActiveState)
    {
        // _life = dictionary["particleLifespan"].asFloat();
        // _emissionRate = _totalParticles / _life;
        float rate = 1.0f / _emissionRate; // 排放速率 = 粒子声明周期 / 粒子数量
        addParticles(emitCount);
         // 每一个粒子的生命递减,
         // 将末尾最后一个生命周期还存在的数据,copy到开头第一个生命周期结束的位置
         // 就是将死去的粒子的位置,用末尾还存在的粒子代替
         // 保证所有存在的粒子都是排在数组的前边,方便后续遍历
         // 接着就是计算各种属性,size,pos,angle,color等
         // 更新渲染数据
         updateParticleQuads()
        // only update gl buffer when visible
        if (_visible && ! _batchNode)
        {
            postStep();
        }
    }
}
// 当添加粒子的时候,因为_particleData已经提前申请好了内存,此时通过下标填充数据即可
void ParticleSystem::addParticles(int count)
{
    //life
    for (int i = start; i < _particleCount ; ++i)
    {
        float theLife = _life + _lifeVar * RANDOM_M11(&RANDSEED);
        _particleData.timeToLive[i] = MAX(0, theLife);
    }
    // positon
    // color
}
void ParticleSystemQuad::updateParticleQuads(){
    // 这里面是纯粹在操作_quads数据,将_particleData正确的填充到_quads里面
}
V3F_C4B_T2F_Quad    *_quads;        // quads to be rendered
struct CC_DLL V3F_C4B_T2F_Quad
{
    /// top left
    V3F_C4B_T2F    tl;
    /// bottom left
    V3F_C4B_T2F    bl;
    /// top right
    V3F_C4B_T2F    tr;
    /// bottom right
    V3F_C4B_T2F    br;
};
void ParticleSystemQuad::postStep()
{
    //                             ↓ 需要关注下这个buffer的来源:ParticleSystemQuad::setupVBOandVAO里面有具体的数据绑定操作等行为
    glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
    //                                                                     ↓之前操作的粒子数据
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_quads[0])*_totalParticles, _quads);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    CHECK_GL_ERROR_DEBUG();
}
void ParticleSystemQuad::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    //quad command
    if(_particleCount > 0)
    {
        _quadCommand.init(_globalZOrder, 
            _texture, // _texture:对应粒子使用的纹理
            getGLProgramState(), _blendFunc, 
            _quads, // 粒子的数据
            _particleCount, // 粒子的个数
            transform, flags
        );
        renderer->addCommand(&_quadCommand);
    }
}
  • 使用到的shader: ShaderPositionTextureColor_noMVP
void main()
{
    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
}
void main()
{
    gl_Position = CC_PMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}

因为QuadCommand也是TrianglesCommand,所以也会参与合批

CCParticleBatchNode

void ParticleSystem::setBatchNode(ParticleBatchNode* batchNode)
{
    if( _batchNode != batchNode ) {
        _batchNode = batchNode; // weak reference
        if( batchNode ) {
            //each particle needs a unique index
            for (int i = 0; i < _totalParticles; i++)
            {
                _particleData.atlasIndex[i] = i;
            }
        }
    }
}
// 在场景中添加一个ParticleBatchNode,这个ParticleBatchNode接管了渲染,
// ParticleBatchNode.addChild(particle) 将顶点数据复制到BatchNode
// ParticleNode需要和ParticleBatchNode使用同样的纹理
void ParticleBatchNode::draw(Renderer* renderer, const Mat4 & /*transform*/, uint32_t flags)
{
    _batchCommand.init(_globalZOrder, getGLProgram(), _blendFunc, 
        _textureAtlas, // 这个图集是需要由Texture2D进行初始化
        _modelViewTransform, flags
    );
    renderer->addCommand(&_batchCommand);
    CC_PROFILER_STOP("CCParticleBatchNode - draw");
}
bool ParticleBatchNode::initWithTexture(Texture2D *tex, int capacity)
{
    // 这个纹理必须是粒子纹理,只有多个相同的粒子
    _textureAtlas = new (std::nothrow) TextureAtlas();
    _textureAtlas->initWithTexture(tex, capacity);
}
// batchCommand的渲染
void BatchCommand::execute()
{
    // Set material
    _shader->use();
    _shader->setUniformsForBuiltins(_mv);
    GL::bindTexture2D(_textureID);
    GL::blendFunc(_blendType.src, _blendType.dst);
    // Draw
    _textureAtlas->drawQuads();
}

优化

粒子的数学运算会消耗一定的性能,这个无法避免。

理论上,只要扩展下Particle.texture使其支持从TextureAtlas里面获取纹理,也能完美实现Particle和Sprite合批,因为底层都是TrianglesCommand,只需要注意下blend混合模式即可。

实现方式,可以考虑追加一个textureAtlas解析,现在是支持textureImageDatatextureFileName的解析,因为studio不支持,所以只能单独开发工具做支撑了。

ParticleBatchNode对性能优化意义不大,因为项目中很少有粒子是连着在一起,不连续就会断批。

ParticleBatchNode会将add进去的Particle的顶点数据放在一个大的buffer里面,这样在填充到renderbuffer里面的时候,就避免了碎片化拷贝内存,会带来一定的性能提升,这和拷贝压缩包比碎文件快,都是一样的道理

目录
相关文章
|
算法 容器
最详细版本|UI2Code智能生成Flutter代码——机器生成代码
作者: 闲鱼技术-上叶,余晏 背景   在《UI2CODE--整体设计》篇中,我们提到UI2Code工程的整体流程。前步图片分析之后,我们可以得到对应的DSL布局描述。利用DSL的资讯,结合IntelliJ Plugin介面工具,面向使用者提供生成对应Flutter代码。
12755 0
|
API 图形学
[√]unity渲染一个文本的细节
[√]unity渲染一个文本的细节
92 0
|
缓存 中间件 atlas
Cocos Creator3.8 项目实战(八)2D UI DrawCall优化详解(上)
Cocos Creator3.8 项目实战(八)2D UI DrawCall优化详解(上)
439 0
Cocos Creator3.8 项目实战(九)2D UI DrawCall优化详解(下)
Cocos Creator3.8 项目实战(九)2D UI DrawCall优化详解(下)
168 0
|
JavaScript Android开发 C++
Cocos Creator 解决热更新资源md5比较引发卡顿问题(一)
Cocos Creator 解决热更新资源md5比较引发卡顿问题
429 0
|
Dart Android开发 开发者
Flutter Tips 小技巧(更新中)(下)
Flutter Tips 小技巧(更新中)(下)
392 0
Flutter Tips 小技巧(更新中)(下)
|
缓存
Flutter Tips 小技巧(更新中)(上)
Flutter Tips 小技巧(更新中)(上)
328 0
Flutter Tips 小技巧(更新中)(上)
|
移动开发 开发工具 git
Flutter Tips 小技巧(更新中)(中)
Flutter Tips 小技巧(更新中)(中)
237 0
|
计算机视觉 索引
NDK OpenGL ES 3.0 开发(二十一):3D 模型加载和渲染
3D 模型的设计一般是由许多小模型拼接组合成一个完整的大模型,一个小模型作为一个独立的渲染单元,我们称这些小模型为网格(Mesh)。
364 0
NDK OpenGL ES 3.0 开发(二十一):3D 模型加载和渲染