2D动画spine渲染原理解析与源码解读

前言:什么是spine?

为了让初学者有更加直观的初步的了解,笔者提供了一个简单的示意图以下:
image.png
实际开发中,由设计人员提供对应的spine编辑器所导出的动画素材,开发人员选用对应的spine运行库对素材进行消费和上屏渲渲染,即是spine所作的事,相比gif、css帧动画、apng具有更增强大的灵活性。css

简单比较以下:web

类型\项 颜色 大小 兼容性 灵活性 成本
GIF 256色
CSS帧动画 真彩 较大 通常
APNG 真彩 部分浏览器兼容
spine2D动画 真彩 大(动画素材+运行库引入) 较优

GIF因为其自己的色彩限制,通常不能知足设计的要求,所以目前大多采用CSS帧动画和APNG的方式处理页面动画,小区域动画采用apng,大区域动画能够考虑采用CSS帧动画或JS动画处理。而厘米秀这里因为业务自己复杂且形象装扮多变,故不得不采用spine2D动画的方案。json

注释:canvas

1)、JSON文件/二进制文件:存储骨架信息,见下文介绍。浏览器

2)、素材图片:相似雪碧图,也可单一素材,可导出一张或多张。架构

3)、与素材图片对应的atlas文件:记录素材图片在雪碧图上的位置信息特征等。一个atlas文件可对应多个素材图片。app

1、基本概念

一、骨架Skeleton:指代的是数据的集合,包含构成此骨架的全部骨骼、插槽、附件及其余信息。编辑器

二、骨骼bones:以官方示意图为例,一我的物自己由多个关节的骨骼组成。除了根骨骼之外,每一个骨骼都有对应的父骨骼,骨骼与骨骼之间的关系最终构形成相似树的结构。函数

image.png

三、插槽slot:一个骨骼bone下可能有多个slot插槽,每一个slot插槽下能够放置一个附件实例。插槽自己的存在有两个重要的意义,一个是灵活的控制渲染顺序,一个是分组同类附件。一个插槽能够有多个附件,但一次只能看到一个。举个简单的栗子,图中手枪所在的位置的插槽是"武器"插槽,而该插槽能够放置不一样的武器附件,例如"手枪"附件或"菜刀"附件。动画

四、附件attachment:slot插槽内当前渲染的附件实例,即真实上屏渲染的实物素材。(可能须要对素材作旋转、偏移、缩放甚至网格化mesh处理)。

五、皮肤skin:skin能够看作是attachment的集合,或者能够认为是attachment的一个映射查询表,一我的物能够由多套skin,经过切换skin的方式去查询不一样的附件映射表,即可以变相的实现人物的全身换装。

其余相关概念:

关键帧:

在编辑器中,动画是借助关键帧完成的,从开始到结束的过渡动画,由spine补间处理。

权重与网格:

权重用于将网格顶点绑定到一个或多个骨骼。变换骨骼时,顶点也会随之变换。权重令网格可以随着操纵骨骼而自动变形,从而让本来复杂的网格变形动画变得与骨骼动画同样简单。

附件类型:
一、区域附件:普通的图片展现附件。
二、点附件:空间中的一个点和旋转,相比骨头的优点能够为不一样的皮肤设置更改位置和旋转,例如不一样的枪从不一样的位置射击。
三、网格附件:支持在图片内设置多边形,以后可操纵多边形的顶点,以有效的方式让图片弯曲和变形。
四、边界框附件:附加到骨骼上的多边形,骨骼变化的时候也会随之变形,可用于撞击检测,建立物理主体等。
五、剪裁附件:剪裁功能让你能够定义一个多边形区域,与边界框附件相似,它会屏蔽绘制顺序中的其余插槽。
六、路径附件:用于设置路径。

其中在业务中比较经常使用到的是区域附件和网格附件。

三种约束:
一、IK约束:反向动力学约束 子骨头终点固定的场景。
二、变换约束:变换约束指的是将对骨骼的世界旋转、移动缩放等复制到多个骨骼上。
三、路径约束:使用路径来调整骨骼变换,骨骼能够沿着路径,也能够调整旋转以指向路径。

2、spine架构和核心类解读

spine总体架构分层以下:

image.png

spine核心类以下:
image.png

读懂上面这张官方所提供的类图,将会对spine的总体架构设计有更加明确的了解和认识。

一、Loading模块:是针对资源加载的处理,一个spine形象的骨架信息导出后,通常会导出为json或者二进制文件的形式,因为json形式纯文本文件过大,因此官方提供了二进制文件导出的形式,而且辅以运行库的代码针对二进制文件进行解析。其次,Loading模块中的atlasAttachmentLoader将会负责atlas文件的解析,因为atlas文件自己是字符串的形式,内部包含雪碧图中素材的位置信息,因此须要解析后与素材创建”关联关系“。例如:Eyes-close素材在picture1.png图片中的x,y位置 旋转角度为z,而构造出来的这种映射关系将用于被实例化attachment的时候消费。

二、Spine Texture Atlas模块:一张素材图映射一个atlasPage,一张素材图中的某个区域块映射一个atlasRegion,而region的详细绘制信息本质上已经在上个模块完成。

三、Rending模块:由渲染层遍历slot进行渲染,这里不作详解,渲染层并不是spine核心库所负责的部分,上屏渲染能够由canvas、webGL或者其余第三方渲染库渲染,例如pixijs。

四、SetupPoseData模块:或者称之为SkeletonData模块,数据源从这里输入进行处理,可是并非最终数据,能够理解为这里对数据作了一层预处理,会将骨骼数据先处理为boneData,插槽数据处理为slotData,固然也有部分数据不须要被再次处理,在这里,也会根据前面生成的atlasRegion去构造出对应的附件实例,存储进skin中,skin本质上为附件映射表。

其次,数据对象自己和实例对象是有差异的。

数据对象是无状态的,可在任意数量的骨架实例间共用。有对应实例数据的数据对象类名称以“Data”结尾,没有对应实例数据的数据对象则没有后缀,如附件、皮肤及动画。

实例对象有许多属性与数据对象相同。数据对象中的属性表明装配姿式,一般不会改动。实例对象中的相同属性表示播放动画时该实例的当前姿式。每一个实例对象保有一个其数据对象参考,用于将实例对象重置回装配姿式。

例如,SkeletonData是数据对象,而Skeleton是实例对象。

五、Instance Data模块:或者称之为Skeleton模块,Skeleton实例自己是渲染层上屏渲染的真实直接数据源,渲染层将读取Skeleton实例上的插槽信息,渲染对应的附件,在这里,许多数据对象已经被处理成对应的实例对象,例如boneData已经被处理为Bone实例,slotData已经被处理为Slot实例;其次,如图中所展现的,Skeleton实例中有两个比较关键的方法,updateWorldTransform和setToSetUpPose。

updateWorldTransform为更新世界变换,本质是触发骨骼位置的计算,因为骨骼位置可能发生旋转偏移,其对应的子骨骼也会受到影响,所以须要更新世界变换从新计算全部骨骼的最新坐标位置。

setToSetUpPose为更新实例到当前初始状态,通常才初始化时或重置人物状态时调用,会将人物形象骨骼装扮等切换为初始最初的状态。

六、Animation模块:动画模块被单独抽离,不只更方便维护和更新实例的状态信息,总体架构逻辑也简洁明了,由动画state实例去触发skeleton实例的更新,接下来skeleton实例调用updateWorldTransform更新世界变化,以后从新上屏渲染。

一个动画实例中由多个timeline构成,这些timeline实例来自于不一样的变种Timeline类,根本上都继承与底层的TimeLine类,因为一个动画过程当中可能涉及多种变化,所以须要对不一样的动画进行划分区别,处理旋转的单独一条timeline,处理缩放的单独一条timeline,等等。而虽然不一样类别动画会抽离成不一样的timeline,可是最终某个时间节点生效触发,全部的timeline"做用"都是同时的。

3、spine源码解读

一、Slot:存储插槽的当前姿式。插槽为{@link Skeleton#drawOrder}目的组织附件,并提供存储附件状态的位置。状态不能存储在附件自己中,由于附件是无状态的,能够跨多个骨架共享。
(deform属性是针对mesh附件的处理信息。)
(经过setToSetUpPose设置初始动做)
(在slot实例里能够直接getAttachment和setAttachment)

二、SlotData:slot实例里用的数据的数据格式,包含index、插槽名称、附件名称、boneData等。

三、BoneData:骨头实例里用的数据格式,包含index(骨头也有index)、骨头名称、父骨头数据、骨头本地转换数据、世界转换的模式。

四、Bone:关键方法updateWorldTransformWith,更新骨骼的世界坐标。包含其余的一些方法,世界坐标和本地坐标的转换,旋转转换等。

五、SkeletonData: Skeleton实例对应的数据格式。包含bones、slots、skins、events、animations、各类约束。提供了
因为是数据对象,仅提供了一些findbone、findslot的方法。

六、Skeleton:根据data新建Bone和Slot。bone有index按顺序创建关联关系。调用setToSetupPose将bone和slot设置到初始位置。会遍历调用bone和slot对应的方法。updateWorldTransform调用bone的updateWorldTransform更新骨骼位置。提供了一些Bone、slot、attachment的get、set方法。
有个update方法,更新time时间。

七、SkeletonBinary:用于读取二进制的skeleton文件。

八、SkeletonBounds:收集每一个可见的BoundingBoxAttachment,并计算其多边形的世界顶点。主要用于碰撞或者命中检测。由渲染层调用。

九、SkeletonCilpping:主要针对ClippingAttachment的处理,由渲染层调用。

十、SkeletonJson:用于解析处理spine导出的skeleton json。须要对应传入一个attachmentLoader让其能构造对应的attachment实例,处理bone、slots、ik、skins、animation等数据。
处理bone和slot构造对应的实例data。
处理skins借助loader生成对应附件实例。
处理 animations生成不一样的timeline实例对象。
最终构造出对应的SkeletonData实例。

十一、Skin:一套皮肤下的全部attachment都在skin实例下,提供了操做skin和attachment的方法。这里操做的方法至关于dictionary,不是改变人物装扮的。

十二、attachment目录:各类附件的处理处理方法,继承于Attachment基类。实际由对应的AttachmentLoader调用对应的附件类方法。
AtlasAttachmentLoader实现了对应方法。

1三、Texture:定义了Texture抽象类,定义一些抽象方法须要被实现。

1四、TextureAtlas:针对atlas文本进行解析处理,实现TextureAtlasReader进行逐行读取,texture
借助外部传入的textureLoader回调来获取对应的纹理。
每块小素材对应一个TextureAtlasPage,素材信息读取解析后构造对应TextureAtlasRegion。

1五、AnimationStateData:存储AnimationState动画更改时要应用的混合(交叉淡入淡出)持续时间。

1六、AnimationState:随着时间调用动画,动画入队等待播放,容许多个动画叠加。
分多个track存储动画、区分不一样动画的timeline,针对event事件的处理逻辑等。

1七、Animation:实现了各类timeline类,Animation负责调用apply方法触发更新,其apply方法会调用各个timeline的apply方法更新。timeline类中实现找到对应关键帧 决定如何渲染。

1八、AssetManager:静态资源管理,包括拉取文本资源、拉取二进制资源、加载纹理。调用TextureAtlas处理atlas文本等。

4、渲染库代码解读

canvas:
一、AssetManager:没有作啥,直接沿用core里的AssetManager
二、canvasTexture:继承Texture。
三、SkeletonRender:传入skeleton数据,渲染画布,
drawImage会遍历drawOrder中的slot,逐个渲染region附件,借助ctx.drawImage API来裁剪和渲染图片。
drawTriangles会计算顶点,渲染调试模式的绿色线条。

threejs:
一、ThreeJsTexture:针对threesjs自己的texture作了一层包裹,处理了一下filter。
二、MeshBatcher:MeshBatcher继承自Threejs自己的Mesh。调用SkeletonMeshMaterial获取材质。
三、SkeletonMesh:SkeletonMeshMaterial继承自ShaderMaterial,这里包含了着色器代码,顶点着色器和片元着色器。
SkeletonMesh继承自Object3D类。
核心渲染函数updateGeometry,skeleton更新世界变化后,调用渲染函数,遍历drawOrder。
RegionAttachment和MeshAttachment会进行渲染,渲染借助MeshBatcher,纹理做为素材传入batchMaterial。

webgl:
一、GLTexture:获取画布,渲染对应的image到画布上。
二、Camera:设置相机位置
三、WebGL:定义了ManagedWebGLRenderingContext,其实就是获取webgl的context上下文。
四、Input:对元素作事件监听,鼠标、touch事件。
五、Shader:自行实现的着色器,片元和顶点。
六、SkeletonRenderer:负责skeleton的上屏渲染,渲染函数须要借助PolygonBatcher来上屏渲染,一样的,只会对RegionAttachment和MeshAttachment会进行渲染。
七、PolygonBatcher:在这里绑定着色器,设置混合模式,绑定一个Mesh实例对象,Mesh为单独封装的mesh类,最终调用的是Mesh暴露的渲染方法。
八、Mesh:单独封装的Mesh类,容许设置指数和顶点,上屏渲染借助context的drawElements和drawArrays方法。
九、SceneRenderer:最上层的调用类,实例化batcher、webgl上下文、shader,实例化SkeletonRenderer,暴露不一样的渲染方法,包括drawSkeleton,drawSkeletonDebug、drawTexture、drawRegion等。

5、spine渲染总体流程图

根据前面的介绍,咱们对基本概念有所了解,而且了解了spine的总体架构设计,针对核心模块进行了介绍,同时对spine核心库源码以及渲染层源码的关键逻辑进行解读,能够整理出spine渲染的总体流程图以下:
image.png

看到这里相信你对spine的总体架构设计,渲染流程都已经有了大体的了解,接下来在明确了底层内部的处理流程后,咱们的下一步工做是实现换装和换动做API,具体如何实现,请听下回分解!

谢谢观看~~

相关文章
相关标签/搜索