今后的几篇郭先生主要说说three.js骨骼动画。three.js骨骼动画十分有意思,但是对于初学者来说,学起来要稍微困难一些,官方文档比较少,网上除了用圆柱体的例子就是引用外部模型的,想要熟练使用骨骼动画就需要不断地探索和练习。这篇是初探three.js骨骼动画,也不深入讲解,先说说它的实现和原理,然后一点一点解读官网案例,骨骼动画官网案例

1. 骨骼动画的实现和原理

1. 骨骼动画的实现

骨骼动画主要有以下三个部分构成:
(1) 几何体–在新版本中这个几何体要求必须是一个BufferGeometry而非Geometry,而骨骼动画需要的几何体还有两个十分重要的属性,

  1. skinWeights : Array
    当在处理一个 SkinnedMesh 时,每个顶点最多可以有 4 个相关的 bones 来影响它。 skinWeights 属性是一个权重队列,顺序同几何体中的顶点保持一致。因而,队列中的第一个 skinWeight 就对应几何体中的第一个顶点。由于每个顶点可以被 4 个 bones 营销,因而每个顶点的 skinWeights 就采用一个 Vector4 表示。skinWeight 矢量中每个元素的取值范围应该在 0 到 1 之间。例如,当设置为 0,骨骼对该顶点的位置没有影响。当设置为 0.5, 则对顶点的影响为 50%。 当设置为 100% 则对顶点的影响是 100%。如果矢量中只有一个骨骼与顶点相关联,则你只需要关注矢量中的第一个元素, 剩余的元素可以忽略,他们的值可以都设置为 0。
  2. skinIndices : Array
    就如同 skinWeights 属性一样。skinWeights 的值也是与几何体的顶点相对应。每个顶点可以最多有 4 个骨骼与之相关联。 因而第一个 skinIndex 就与几何体的第一个顶点相关联,skinIndex 的值就指明了影响该顶点的骨骼是哪个。例如,第一个顶点的值是 ( 10.05, 30.10, 12.12 ),第一个 skinIndex 的值是( 10, 2, 0, 0 ),第一个 skinWeight 的值是 ( 0.8, 0.2, 0, 0 )。上述值表明第一个顶点受到mesh.bones[10]骨骼的影响有 80%, 受到 skeleton.bones[2] 的影响是 20%,由于另外两个 skinWeight 的值是 0,因而他们对顶点没有任何影响。

(2) 其材质必须支持蒙皮,并且已经启用了蒙皮,既skinning = true;
(3) 创建骨骼和骨架

2. 骨骼动画的原理

骨骼(Bone)其实就是一个Object3D对象,可以把骨架看成是人体骨架,假如脊柱的根节点,那么大腿就是下一级节点,小腿就是更下一级的节点,如果大腿转动,那么小腿在世界坐标系必然会动,而小腿动,不一定影响大腿。
现在我们假如有一个几何体(这个几何体加上带蒙皮的材质就是我们的腿的网格),想让这个几何体跟着这个骨骼运动,那么这个动画就是骨骼动画,现在我们假设bones[0]为大腿上端点,bones[1]为大小腿关节点,bones[2]为小腿下端点,这里如果我们把腿看成是圆柱体(官方案例就是这样做的),将极大的降低了难度,让heightSegments为2(就是分两段)也就生成了沿高度分布的3层点。

我们将最上层点对应的skinIndices设置成0,skinWeights设置成1。中间层点对应的skinIndices设置成1,skinWeights设置成1。最下层点对应的skinIndices设置成2,skinWeights设置成1。这样几何体的顶点就和骨骼的端点建立了联系。

2. 官网上的骨骼动画

1. 初始化蒙皮网格

  1. //这是生成蒙皮网格的主方法
  2. initBones() {
  3. //下面是一些会用到的参数
  4. var segmentHeight = 8; //每段的高度
  5. var segmentCount = 4; //段数
  6. var height = segmentHeight * segmentCount; //总高度
  7. var halfHeight = height * 0.5; //一般高度
  8.  
  9. var sizing = {
  10. segmentHeight: segmentHeight,
  11. segmentCount: segmentCount,
  12. height: height,
  13. halfHeight: halfHeight
  14. };
  15.  
  16. var geometry = this.createGeometry( sizing ); //这是生成几何体的方法,主要是根据顶点生成对应的skinIndex和skinWeight属性
  17. var bones = this.createBones( sizing ); //这是生成骨骼的方法
  18. mesh = this.createMesh( geometry, bones ); //这是生成蒙皮网格的方法
  19.  
  20. mesh.scale.multiplyScalar( 1 );
  21. scene.add( mesh );
  22.  
  23. this.render();
  24. document.getElementById("loading").style.display = "none";
  25. },

2. 生成带有skinIndex和skinWeight属性的几何体

  1. createGeometry(sizing) {
  2. //创建一个圆柱体
  3. var geometry = new THREE.CylinderBufferGeometry(
  4. 5, // 上面半径
  5. 5, // 下面半径
  6. sizing.height, // 总高度
  7. 8, // 圆形面分段数
  8. sizing.segmentCount * 3, // 沿高度的分段数4*3
  9. true // 无上下面
  10. );
  11.  
  12. var position = geometry.attributes.position; //圆柱体顶点位置集合
  13.  
  14. var vertex = new THREE.Vector3(); //创建一个三维向量用于保存顶点坐标
  15.  
  16. var skinIndices = []; //顶点索引聚合
  17. var skinWeights = []; //顶点权重聚合
  18.  
  19. for ( var i = 0; i < position.count; i ++ ) { //遍历顶点
  20.  
  21. vertex.fromBufferAttribute( position, i ); //依次取出每个点
  22.  
  23. var y = ( vertex.y + sizing.halfHeight ); //y保存相对于圆柱体底面的高度值。
  24.  
  25. //这两行比较重要
  26. var skinIndex = Math.floor( y / sizing.segmentHeight ); //高度除以总高度在向下取整,得到当前的skinIndex
  27. var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight; //当前的y值占该段的百分比
  28.  
  29. skinIndices.push( skinIndex, skinIndex + 1, 0, 0 ); //该点关联bone[skinIndex]和bone[skinIndex+1]
  30. skinWeights.push( 1 - skinWeight, skinWeight, 0, 0 ); //关联bone[skinIndex]的比重为1 - skinWeight,关联bone[skinIndex+1]的比重为skinWeight。
  31. //举个例子,第一个y值刚好为0。那么skinIndex为0,skinWeight也为0。所以呢该点相关的骨骼索引为0和1,权重分别是1和0,也就是该点只与bone[0]有关。
  32. //再比如y值为4,那么skinIndex为0,skinWeight也为0.5,所以呢该点相关的骨骼索引为0和1,权重分别是0.5和0.5,也就是该点与bone[0]和bone[1]都相关。其实也很容易理解,因为4恰好在该分段的中间,所以决定于两个骨骼点的状态。
  33.  
  34. }
  35.  
  36. geometry.setAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( skinIndices, 4 ) ); //几何体中添加skinIndex属性
  37. geometry.setAttribute( 'skinWeight', new THREE.Float32BufferAttribute( skinWeights, 4 ) ); //几何体中添加skinWeight属性
  38.  
  39. return geometry;
  40. },

3. 生成骨骼

  1. createBones(sizing) {
  2. bones = []; //骨骼数组
  3.  
  4. var prevBone = new THREE.Bone(); //根骨骼节点
  5. bones.push( prevBone ); //数组中添加根骨骼节点
  6. prevBone.position.y = - sizing.halfHeight; //为根骨骼添加位置
  7.  
  8. for ( var i = 0; i < sizing.segmentCount; i ++ ) { //遍历分段
  9.  
  10. var bone = new THREE.Bone(); //创建骨骼节点
  11. bone.position.y = sizing.segmentHeight; //为骨骼节点添加本地位置 虽然本地设置的位置都是一样的,但是由于这些骨骼都是父子关系,所以在世界坐标系上位置不同
  12. bones.push( bone ); //数组中继续添加骨骼
  13. prevBone.add( bone ); //根骨骼添加当前骨骼
  14. prevBone = bone; //再将当前骨骼赋值给根骨骼
  15.  
  16. }
  17. return bones;
  18. },

4. 创建蒙皮网格并添加骨骼显示助手

  1. createMesh(geometry, bones) {
  2. //创建一个带蒙皮的材质
  3. var material = new THREE.MeshPhongMaterial( {
  4. skinning: true, //重点
  5. color: 0x156289,
  6. emissive: 0x072534,
  7. side: THREE.DoubleSide,
  8. flatShading: true
  9. } );
  10.  
  11. var mesh = new THREE.SkinnedMesh( geometry, material ); //创建蒙皮网格
  12. var skeleton = new THREE.Skeleton( bones ); //创建骨架
  13.  
  14. mesh.add( bones[ 0 ] ); //网格添加根骨骼节点(此例bones[0]为根节点)
  15.  
  16. mesh.bind( skeleton ); //网格绑定骨架
  17.  
  18. skeletonHelper = new THREE.SkeletonHelper( mesh ); //创建骨骼显示助手
  19. skeletonHelper.material.linewidth = 2;
  20. scene.add( skeletonHelper );
  21.  
  22. return mesh;
  23. },

最后就是使用gui进行界面控制,这里只说一下蒙皮网格有一个pose()方法,使用后骨架还原为初始状态。官方的骨骼动画解析就到此为止,后面还会继续说说骨骼动画。

转载请注明地址:郭先生的博客

three.js之初探骨骼动画的更多相关文章

  1. 基于babylon3D模型研究3D骨骼动画(1)

    3D骨骼动画是实现较为复杂3D场景的重要技术,Babylon.js引擎内置了对骨骼动画的支持,但Babylon.js使用的骨骼动画的模型多是从3DsMax.Blender等3D建模工具转换而来,骨骼动 ...

  2. Cocos2d-js 开发记录:骨骼动画载入

    不得不说cocos2d-js的文档实在是少,骨骼动画的载入和C++版本的好像还有些不同不能直接依样画葫芦. 一个由cocos studio编辑生成的骨骼动画一般会包含如下几个文件: .ExportJs ...

  3. 基于Babylon.js编写简单的骨骼动画生成器

    使用骨骼动画技术可以将网格的顶点分配给若干骨头,通过给骨头设定关键帧和父子关系,可以赋予网格高度动态并具有传递性的变形 效果.这里结合之前的相关研究在网页端使用JavaScript实现了一个简单的骨骼 ...

  4. three.js 自制骨骼动画(二)

    上一篇说了一下自制骨骼动画,这一篇郭先生使用帧动画让骨骼动画动起来.帧动画是一套比较完善的动画剪辑方法,详细我的api我们就不多说了,网上有很多例子,自行查找学习.在线案例请点击博客原文.话不多说先上 ...

  5. 关于Cocos Creator用js脚本代码播放骨骼动画的步骤和注意事项

    步骤: 1.用cc.find()方法找到相应的骨骼动画节点,并把这个对象赋值给一个var出来的新对象. 具体代码:var spineboy_anim = cc.find("UI_Root/a ...

  6. three.js 自制骨骼动画(一)

    上一篇郭先生解析了一下官方的骨骼动画案例,这篇郭先生就要做一个稍微复杂一点的骨骼动画了,就拿一个小人下手吧.在线案例请点击博客原文.话不多说先上大图 骨骼动画在GUI上面都有体现.制作骨骼动画的步骤在 ...

  7. cocos2dx骨骼动画Armature源码分析(一)

    源码分析一body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-to ...

  8. HTML5骨骼动画Demo | 使用min2d、createjs、pixi播放spine动画

    Spine做骨骼动画是比较流行的,使用起来可能相对复杂,但功能毕竟强大,所以市场占有率较大. 在unity.cocos2d.starling中使用spine已经很成熟了,而HTML5这一块可能刚刚起步 ...

  9. 集成骨骼动画Spine的几点经验

    最近开始用cantk做些复杂的游戏,其中一个游戏的DragonBones骨骼动画的JSON文件就达600K,导出之后显示各种不正常,可能是太复杂了,有些方面达到了DragonBones的极限.拿到官方 ...

随机推荐

  1. H5调用手机的相机/摄像/录音等功能 _input:file的capture属性说明

    H5使用input标签调用系统默许相机,摄像,录音功能.使用input:file标签, 去调用系统默认相机,摄像,录音功能,其实是有个capture属性,直接说明需要调用什么功能: <input ...

  2. 第三方 CSS 并不安全

    最近一段时间,关于 通过 CSS 创建 “keylogger”(键盘记录器) 的讨论很多. 有些人呼吁浏览器厂商去“修复”它.有些人则深入研究,表示它仅能影响通过类 React 框架建立的网站,并指责 ...

  3. css自动省略号...,通过css实现单行、多行文本溢出显示省略号

    网页开发过程中经常会遇到需要把多行文字溢出显示省略号,这篇文章将总结通过多种方法实现文本末尾省略号显示. 一.单行文本溢出显示省略号(…) 省略号在ie中可以使用text-overflow:ellip ...

  4. JAVA基础笔记10-11-12-13-14

    十.今日内容介绍 1.继承 2.抽象类 3.综合案例---员工类系列定义 01继承的概述 *A:继承的概念 *a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 *b:在J ...

  5. django模板中变更数据库信息后,如何把变更后的信息同步更新到数据库

    我们在基于django开发项目的过程中,经常会遇到数据库表字段增加,删除,或者修改的情况,以及字段属性更改的情况,因为django基于ORM模式来操作数据库的, 传统上如果django项目中的数据库m ...

  6. PowerJob 的故事开始:“玩够了,才有精力写开源啊!”

    本文适合有 Java 基础知识的人群 作者:HelloGitHub-Salieri HelloGitHub 推出的<讲解开源项目>系列.经过几番的努力和沟通,终于邀请到分布式任务调度与计算 ...

  7. Centos7 python 安装 Ignoring ensurepip failure: pip 9.0.1 requires SSL/TLS

    安装python时出现Ignoring ensurepip failure:pip required SSL/TLS 因为没有安装OpenSSL 使用yum install openssl-devel

  8. HotSpot VM垃圾收集器

    最常用的HotSpot VM垃圾收集器是分代垃圾收集.该方案是基于两个观察事实. 大多数分配对象的存活时间很短. 存活时间久的对象很少引用存活时间短的对象. 上述两个观察事实统称为弱分代假设(Weak ...

  9. IOS10 window.navigator.geolocation.getCurrentPosition 无法定位问题

    在iOS 10中,苹果对webkit定位权限进行了修改,所有定位请求的页面必须是https协议的. 如果是非https网页,在http协议下通过HTML5原生定位接口会返回错误,也就是无法正常定位到用 ...

  10. 创建MongoDB副本集教程

    今天有时间搞了一下mongoDB的副本集,遇到好多坑,写下此文,方便日后查阅! 本教程是在windows环境下安装测试的(我是本机一台 + 两台虚拟机) 本机:10.53.8.159 虚拟机一:10. ...