前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flipagram 涂鸦特效逆向分析

Flipagram 涂鸦特效逆向分析

作者头像
MelonTeam
发布2018-01-04 16:40:48
1.2K0
发布2018-01-04 16:40:48
举报
文章被收录于专栏:MelonTeam专栏MelonTeam专栏

背景

Flipagram是美国一款非常火爆的短视频应用,一度登顶美国appstore榜首。前段时间又被今日头条重金收购,被当做今日头条发展海外短视频、提升国际影响力的发力点。本着好奇的心情体验了一下这款APP,印象最深的是视频编辑能力比较强,特别是动态涂鸦效果感觉非常惊艳,打算好好研究一下,优化改进一下手Q现有的涂鸦效果。

Flipagram特效

Untitled
Untitled

仿涂鸦特效

初步推测:系统自带的粒子效果+手势,在手指移动的过程中创建不同效果的粒子发射机,粒子发射机发射不同效果的粒子。

IOS粒子效果

系统自带的粒子效果实现主要的类是:CAEmitterBehaviorCAEmitterLayerCAEmitterCell他们的作用分别是,定义粒子发射机的行为、设置发射机的特征、设置粒子的具体特效。

使用比较简单:

代码语言:javascript
复制
    //Create the root layer
    rootLayer = [CALayer layer];

    //Set the root layer's attributes
    rootLayer.bounds = CGRectMake(0, 0, 640, 480);
    CGColorRef color = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1);
    rootLayer.backgroundColor = color;
    CGColorRelease(color);

    //Load the spark image for the particle
    NSImage *image = [NSImage imageNamed:@"tspark"];
    CGImageRef img = [image CGImageForProposedRect:nil context:nil hints:nil];

    mortor = [CAEmitterLayer layer];
    mortor.emitterPosition = CGPointMake(320, 0);
    mortor.renderMode = kCAEmitterLayerAdditive;

    //Invisible particle representing the rocket before the explosion
    CAEmitterCell *rocket = [CAEmitterCell emitterCell];
    rocket.emissionLongitude = M_PI / 2;
    rocket.emissionLatitude = 0;
    rocket.lifetime = 1.6;
    rocket.birthRate = 1;
    rocket.velocity = 400;
    rocket.velocityRange = 100;
    rocket.yAcceleration = -250;
    rocket.emissionRange = M_PI / 4;
    color = CGColorCreateGenericRGB(0.5, 0.5, 0.5, 0.5);
    rocket.color = color;
    CGColorRelease(color);
    rocket.redRange = 0.5;
    rocket.greenRange = 0.5;
    rocket.blueRange = 0.5;

    //Name the cell so that it can be animated later using keypath
    rocket.name = @"rocket";

    //Flare particles emitted from the rocket as it flys
    CAEmitterCell *flare = [CAEmitterCell emitterCell];
    flare.contents = (__bridge id)img;
    flare.emissionLongitude = (4 * M_PI) / 2;
    flare.scale = 0.4;
    flare.velocity = 100;
    flare.birthRate = 45;
    flare.lifetime = 1.5;
    flare.yAcceleration = -350;
    flare.emissionRange = M_PI / 7;
    flare.alphaSpeed = -0.7;
    flare.scaleSpeed = -0.1;
    flare.scaleRange = 0.1;
    flare.beginTime = 0.01;
    flare.duration = 0.7;

    //The particles that make up the explosion
    CAEmitterCell *firework = [CAEmitterCell emitterCell];
    firework.contents = (__bridge id)img;
    firework.birthRate = 9999;
    firework.scale = 0.6;
    firework.velocity = 130;
    firework.lifetime = 2;
    firework.alphaSpeed = -0.2;
    firework.yAcceleration = -80;
    firework.beginTime = 1.5;
    firework.duration = 0.1;
    firework.emissionRange = 2 * M_PI;
    firework.scaleSpeed = -0.1;
    firework.spin = 2;

    //Name the cell so that it can be animated later using keypath
    firework.name = @"firework";

    //preSpark is an invisible particle used to later emit the spark
    CAEmitterCell *preSpark = [CAEmitterCell emitterCell];
    preSpark.birthRate = 80;
    preSpark.velocity = firework.velocity * 0.70;
    preSpark.lifetime = 1.7;
    preSpark.yAcceleration = firework.yAcceleration * 0.85;
    preSpark.beginTime = firework.beginTime - 0.2;
    preSpark.emissionRange = firework.emissionRange;
    preSpark.greenSpeed = 100;
    preSpark.blueSpeed = 100;
    preSpark.redSpeed = 100;

    //Name the cell so that it can be animated later using keypath
    preSpark.name = @"preSpark";

    //The 'sparkle' at the end of a firework
    CAEmitterCell *spark = [CAEmitterCell emitterCell];
    spark.contents = (__bridge id)img;
    spark.lifetime = 0.05;
    spark.yAcceleration = -250;
    spark.beginTime = 0.8;
    spark.scale = 0.4;
    spark.birthRate = 10;

    preSpark.emitterCells = @[spark];
    rocket.emitterCells = @[flare, firework, preSpark];
    mortor.emitterCells = @[rocket];
    [rootLayer addSublayer:mortor];

    //Set the view's layer to the base layer
    theView.layer = rootLayer;
    [theView setWantsLayer:YES];

    //Force the view to update
    [theView setNeedsDisplay:YES];

具体效果:

Untitled1
Untitled1

Flipagram特效参数分析

粒子效果的参数非常多,要实现和Flipagram一样的效果,设置参数是一件非常繁琐的事情,借用粒子效果的工具分析了一种类似效果的具体参数,主要包含4个方面的参数:发射机、粒子、色彩、纹理 16个发射机参数(截取部分显示):

屏幕快照 2017-04-28
下午2.05.28
屏幕快照 2017-04-28 下午2.05.28

10个粒子参数(截取部分显示):

屏幕快照 2017-04-28
下午2.05.39
屏幕快照 2017-04-28 下午2.05.39

12个色彩参数(截取部分显示):

屏幕快照 2017-04-28
下午2.05.46
屏幕快照 2017-04-28 下午2.05.46

纹理设置:

屏幕快照 2017-04-28
下午2.05.53
屏幕快照 2017-04-28 下午2.05.53

小结:通过类似粒子效果的参数分析,发现系统接口的参数比较少,根本实现不了这种稍微复杂一点的效果。

逆向分析

UI层级分析

通过分析Flipagram的涂鸦页面的UI层级,发现和涂鸦渲染层的类是FGDrawControlsView

708FF504-D266-4C76-A31A-
E75094AAE585
708FF504-D266-4C76-A31A- E75094AAE585

相关类

通过反汇编分析了和渲染层FGDrawControlsView相关的主要类:FGDrawEnginePathFactoryFGDrawEnginePathFGDrawEngine

056C8FF2-D711-4FAA-BA7F-
8451A4C8966D
056C8FF2-D711-4FAA-BA7F- 8451A4C8966D

FGDrawEnginePathFactory是一个负责创建具体特效路径的工厂类,比如:烟花效果的路径、不同画刷的路径、表情效果的路径、沙子效果的路径等等。 FGDrawEnginePath设置粒子发射机的特性和粒子特效参数,比如:颜色、画刷参数、弧度等,同时维护了一条路径的矢量点阵信息。 FGDrawEngine内部利用了OpenGL把发射机参数、粒子效果参数、色彩参数、纹理参数的具体效果渲染出来。

小结

IOS自带的粒子效果使用比较简单,但是效果也比较单一,很难实现酷炫的效果。Flipagram的涂鸦特效实现是在手指移动的过程中创建不同效果的粒子发射机,粒子发射机发射不同效果的粒子。其中FGDrawEnginePath主要负责管理路径和特效信息,FGDrawEngine负责渲染具体的效果。

后续工作

继续分析FGDrawEngine的内部接口,利用OpenGL实现一个粒子特效引擎,最终实现Flipagram的涂鸦特效。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flipagram特效
  • 仿涂鸦特效
    • IOS粒子效果
      • Flipagram特效参数分析
      • 逆向分析
        • UI层级分析
          • 相关类
            • 小结
            • 后续工作
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档