[√]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里面的时候,就避免了碎片化拷贝内存,会带来一定的性能提升,这和拷贝压缩包比碎文件快,都是一样的道理

目录
相关文章
|
2月前
|
Dart 开发者
flutter_鸿蒙next_Dart基础③函数
本文深入探讨了 Dart 编程语言中的函数概念,包括函数的基本定义、参数、返回值以及高级使用场景。通过 Flutter 和鸿蒙的实际案例,介绍了函数的定义、调用、可选参数、命名参数、匿名函数和高阶函数等内容,帮助读者更好地理解和应用 Dart 函数。
141 1
|
3月前
|
开发框架 UED 计算机视觉
flutter:图片&stful 生命周期 (三)
本文档介绍了如何在Flutter中处理图片,包括加载网络图片、本地图片、创建圆形图片和带有圆角的图片,以及如何配置`pubspec.yaml`文件来添加资源文件。还展示了如何使用`AssetImage`对象来显示本地资源图片,并通过代码示例详细说明了这些操作的实现方法。最后,简要介绍了StatefulWidget的生命周期。
|
API 图形学
[√]unity渲染一个文本的细节
[√]unity渲染一个文本的细节
98 0
|
缓存 中间件 atlas
Cocos Creator3.8 项目实战(八)2D UI DrawCall优化详解(上)
Cocos Creator3.8 项目实战(八)2D UI DrawCall优化详解(上)
475 0
Cocos Creator3.8 项目实战(九)2D UI DrawCall优化详解(下)
Cocos Creator3.8 项目实战(九)2D UI DrawCall优化详解(下)
194 0
|
缓存 容器
Flutter使用过程中的一些坑
在Flutter中使用图片时,可能会遇到各种问题和坑,以下是一些常见的问题及解决方法,以及相应的代码示例
Flutter使用过程中的一些坑
|
XML 存储 缓存
如何让 Flutter 应用更好地使用 SVG?
SVG 作为一个强大的矢量图标准格式,在图片清晰度的表现力上有着位图无法比拟的优势。那么是否 SVG 就是绝对的首选了呢?事实可能并非如此。本文将带大家了解 SVG 在 Flutter 应用中的性能问题,分享 UC 浏览器内核技术团队在 Flutter 应用中改进 SVG 应用的探索实践。
3381 0
如何让 Flutter 应用更好地使用 SVG?
Unity-UI 架构优化小技巧(二)
Unity-UI 架构优化小技巧
161 0
Unity-UI 架构优化小技巧(二)
|
XML 前端开发 定位技术
Android MVVM框架使用(十三)UI更新 (App启动白屏优化、适配Android10.0深色模式)
Android MVVM框架使用(十三)UI更新 (App启动白屏优化、适配Android10.0深色模式)
421 0

热门文章

最新文章