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

骨骼动画在GUI上面都有体现。制作骨骼动画的步骤在官方案例中已经看到了,这里在回忆一下。骨骼动画的基本步骤

  1. 创建一个BufferGeometry,并添加skinIndex和skinWeight两个属性。
  2. 创建骨骼Bone
  3. 建蒙皮材质Material
  4. 根据BufferGeometry和Material创建蒙皮网格SkinnedMesh
  5. 根据Bone创建骨架Skeleton
  6. SkinnedMesh添加骨骼根节点
  7. innedMesh绑定骨架

总结起来差不多就这7步,接下来我们一步一步从零创建一个这样的小人

1. 创建一个BufferGeometry,并添加skinIndex和skinWeight两个属性。

我们有几种方案创建这样的复杂几何体。

  1. 使用threeBSP创建这样的几何体,优点是二元操作更加的强大,能做出复杂几何体,缺点是性能开销大。
  2. 使用BufferGeometry的merge方法,将几何体合并,优点是性能好,缺点是只能将几何体合并。
  3. 直接引用模型

我们采用第二种方法。

  1. let head = new THREE.SphereGeometry(3, 60, 40);
  2. let eye_l = new THREE.BoxGeometry(2, 1, 1);
  3. let eye_r = new THREE.BoxGeometry(2, 1, 1);
  4. let mouth = new THREE.BoxGeometry(2, 0.4, 1);
  5. head.merge(eye_l, new THREE.Matrix4().makeTranslation(2, 1, 1))
  6. head.merge(eye_r, new THREE.Matrix4().makeTranslation(2, 1, -1))
  7. head.merge(mouth, new THREE.Matrix4().makeTranslation(2, -1, 0))
  8.  
  9. let body = new THREE.BoxGeometry(5, 10, 8, 1, 20, 1);
  10. let leg_l = new THREE.CylinderGeometry(2, 2, 14, 8, 14);
  11. let leg_r = new THREE.CylinderGeometry(2, 2, 14, 8, 14);
  12. let arm_l = new THREE.CylinderGeometry(1, 1, 12, 8, 11);
  13. let arm_r = new THREE.CylinderGeometry(1, 1, 12, 8, 11);
  14. let human = body.clone();
  15.  
  16. human.merge(head, new THREE.Matrix4().makeTranslation(0, 8, 0));
  17. human.merge(leg_l, new THREE.Matrix4().makeTranslation(0, -12, 2.1));
  18. human.merge(leg_r, new THREE.Matrix4().makeTranslation(0, -12, -2.1));
  19. human.merge(arm_l, new THREE.Matrix4().makeTranslation(0, -1, 5.3));
  20. human.merge(arm_r, new THREE.Matrix4().makeTranslation(0, -1, -5.3));
  21.  
  22. human = new THREE.BufferGeometry().fromGeometry(human);

这里创建小人几何体完毕。接下来是并添加skinIndex和skinWeight两个属性。

  1. var skinIndices = [];
  2. var skinWeights = [];
  3. let position = human.attributes.position;
  4. var vertex = new THREE.Vector3();
  5.  
  6. for(let i=0; i<position.count; i++) {
  7. vertex.fromBufferAttribute(position, i);
  8. if(vertex.z > 4.3 && vertex.y >= 0) {
  9. skinIndices.push(9,0,0,0);
  10. skinWeights.push(1,0,0,0);
  11. } else if (vertex.z < -4.3 && vertex.y >= 0) {
  12. skinIndices.push(10,0,0,0);
  13. skinWeights.push(1,0,0,0);
  14. } else if (vertex.z > 4.3 && vertex.y < 0) {
  15. skinIndices.push(11,0,0,0);
  16. skinWeights.push(1,0,0,0);
  17. } else if (vertex.z < -4.3 && vertex.y < 0) {
  18. skinIndices.push(12,0,0,0);
  19. skinWeights.push(1,0,0,0);
  20. } else if (vertex.y <= 5 && vertex.y >= -5) {
  21. let w = (vertex.y + 5) / 10;
  22. skinIndices.push(0,2,0,0);
  23. skinWeights.push(Math.sqrt(w),1-Math.sqrt(w),0,0);
  24. } else if (vertex.y > 5) {
  25. skinIndices.push(1,0,0,0);
  26. skinWeights.push(1,0,0,0);
  27. } else if(vertex.y < -5 && vertex.y >= -12 && vertex.z > 0) {
  28. skinIndices.push(3,0,0,0);
  29. skinWeights.push(1,0,0,0);
  30. } else if (vertex.y < -12 && vertex.z > 0) {
  31. skinIndices.push(5,0,0,0);
  32. skinWeights.push(1,0,0,0);
  33. } else if (vertex.y < -5 && vertex.y >= -12 && vertex.z < 0) {
  34. skinIndices.push(4,0,0,0);
  35. skinWeights.push(1,0,0,0);
  36. } else {
  37. skinIndices.push(6,0,0,0);
  38. skinWeights.push(1,0,0,0);
  39. }
  40. }
  41.  
  42. human.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices, 4));
  43. human.setAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeights, 4));

这里根据几何体顶点的x,y,z值来添加索引和权重,

2. 创建骨骼Bone

骨骼的节点如下图

其中bone1为根节点。

  1. let bones = [];
  2. let bone1 = new THREE.Bone(); //胸
  3. bone1.position.y = 5;
  4. let bone2 = new THREE.Bone(); //头
  5. bone2.position.y = 3;
  6. let bone3 = new THREE.Bone(); //尾椎
  7. bone3.position.y = -10;
  8.  
  9. let bone4 = new THREE.Bone(); //左腿上
  10. bone4.position.y = -0.1;
  11. bone4.position.z = 2.1;
  12. let bone5 = new THREE.Bone(); //右腿上
  13. bone5.position.y = -0.1;
  14. bone5.position.z = -2.1;
  15. let bone6 = new THREE.Bone(); //左腿中
  16. bone6.position.y = -7;
  17. let bone7 = new THREE.Bone(); //右腿中
  18. bone7.position.y = -7;
  19. let bone8 = new THREE.Bone(); //左腿下
  20. bone8.position.y = -7;
  21. let bone9 = new THREE.Bone(); //右腿下
  22. bone9.position.y = -7;
  23.  
  24. let bone10 = new THREE.Bone(); //左臂上
  25. bone10.position.z = 5.3;
  26. let bone11 = new THREE.Bone(); //右臂上
  27. bone11.position.z = -5.3;
  28. let bone12 = new THREE.Bone(); //左臂中
  29. bone12.position.y = -6;
  30. let bone13 = new THREE.Bone(); //右臂中
  31. bone13.position.y = -6;
  32. let bone14 = new THREE.Bone(); //左臂下
  33. bone14.position.y = -6;
  34. let bone15 = new THREE.Bone(); //右臂下
  35. bone15.position.y = -6;
  36.  
  37. bone1.add(bone2);
  38. bone1.add(bone3);
  39. bone1.add(bone10)
  40. bone1.add(bone11)
  41.  
  42. bone3.add(bone4);
  43. bone3.add(bone5);
  44.  
  45. bone4.add(bone6);
  46. bone5.add(bone7);
  47. bone6.add(bone8);
  48. bone7.add(bone9);
  49. bone10.add(bone12)
  50. bone11.add(bone13)
  51. bone12.add(bone14)
  52. bone13.add(bone15)
  53.  
  54. bones.push(bone1);
  55. bones.push(bone2);
  56. bones.push(bone3);
  57. bones.push(bone4);
  58. bones.push(bone5);
  59. bones.push(bone6);
  60. bones.push(bone7);
  61. bones.push(bone8);
  62. bones.push(bone9);
  63. bones.push(bone10);
  64. bones.push(bone11);
  65. bones.push(bone12);
  66. bones.push(bone13);
  67. bones.push(bone14);
  68. bones.push(bone15);

3. 创建蒙皮材质Material

这个比较简单,就是打开材质的蒙皮属性skinning: true。

  1. let material = new THREE.MeshPhongMaterial({
  2. skinning: true,
  3. color: 0x156289,
  4. emissive: 0x072534,
  5. side: THREE.DoubleSide,
  6. flatShading: true,
  7. // wireframe: true,
  8. })

4. 根据BufferGeometry和Material创建蒙皮网格SkinnedMesh

  1. mesh = new THREE.SkinnedMesh(human, material);

5. 根据Bone创建骨架Skeleton

  1. let skeleton = new THREE.Skeleton(bones);

6. SkinnedMesh添加骨骼根节点

  1. mesh.add(bones[0]);

7. SkinnedMesh绑定骨架

  1. mesh.bind(skeleton);

8. 添加界面交互GUI

  1. var bones = mesh.skeleton.bones;
  2.  
  3. gui.add( mesh, "pose" );
  4.  
  5. gui.add(bones[0].rotation, 'y', bones[0].rotation.y - Math.PI/4, bones[0].rotation.y + Math.PI/4);
  6. gui.add(bones[0].rotation, 'z', bones[0].rotation.z - Math.PI/2, bones[0].rotation.z);
  7. gui.add(bones[0].position, 'y', bones[0].position.y - 21.5, bones[0].position.y);
  8.  
  9. gui.add(bones[1].rotation, 'y', bones[1].rotation.y - Math.PI/4, bones[1].rotation.y + Math.PI/4);
  10. gui.add(bones[1].rotation, 'z', bones[1].rotation.z - Math.PI/6, bones[1].rotation.z + Math.PI/6);
  11.  
  12. gui.add(bones[2].rotation, 'y', bones[2].rotation.y - Math.PI/6, bones[2].rotation.y + Math.PI/6);
  13.  
  14. gui.add(bones[3].rotation, 'z', bones[3].rotation.z - Math.PI/3, bones[3].rotation.z + Math.PI/3);
  15. gui.add(bones[4].rotation, 'z', bones[4].rotation.z - Math.PI/3, bones[4].rotation.z + Math.PI/3);
  16. gui.add(bones[5].rotation, 'z', bones[5].rotation.z - Math.PI/3, bones[5].rotation.z);
  17. gui.add(bones[6].rotation, 'z', bones[6].rotation.z - Math.PI/3, bones[6].rotation.z);
  18.  
  19. gui.add(bones[9].rotation, 'x', bones[9].rotation.x - Math.PI, bones[9].rotation.x);
  20. gui.add(bones[9].rotation, 'y', bones[9].rotation.y - Math.PI/2, bones[9].rotation.y + Math.PI/4);
  21. gui.add(bones[9].rotation, 'z', bones[9].rotation.z - Math.PI/3, bones[9].rotation.z + Math.PI);
  22.  
  23. gui.add(bones[10].rotation, 'x', bones[10].rotation.x, bones[10].rotation.x + Math.PI);
  24. gui.add(bones[10].rotation, 'y', bones[10].rotation.y - Math.PI/4, bones[10].rotation.y + Math.PI/2);
  25. gui.add(bones[10].rotation, 'z', bones[10].rotation.z - Math.PI/3, bones[10].rotation.z + Math.PI);
  26.  
  27. gui.add(bones[11].rotation, 'z', bones[11].rotation.z, bones[11].rotation.z + Math.PI/4*3);
  28. gui.add(bones[12].rotation, 'z', bones[12].rotation.z, bones[12].rotation.z + Math.PI/4*3);
  29.  
  30. gui.__controllers[0].name("重置身体");
  31. gui.__controllers[1].name("身体-旋转");
  32. gui.__controllers[2].name("身体-前趴");
  33. gui.__controllers[3].name("身体-下移");
  34.  
  35. gui.__controllers[4].name("头-左右转");
  36. gui.__controllers[5].name("头-上下转");
  37.  
  38. gui.__controllers[6].name("腰-扭动");
  39.  
  40. gui.__controllers[7].name("左大腿");
  41. gui.__controllers[8].name("右大腿");
  42. gui.__controllers[9].name("左小腿");
  43. gui.__controllers[10].name("右小腿");
  44.  
  45. gui.__controllers[11].name("左大臂-侧平举");
  46. gui.__controllers[12].name("左大臂-内旋");
  47. gui.__controllers[13].name("左大臂-前平举");
  48.  
  49. gui.__controllers[14].name("右大臂-侧平举");
  50. gui.__controllers[15].name("右大臂-内旋");
  51. gui.__controllers[16].name("右大臂-前平举");
  52.  
  53. gui.__controllers[17].name("左小臂");
  54. gui.__controllers[18].name("右小臂");

这样就完成了一个骨骼动画的模型,是不是很简单呢。下篇我们继续完善它,添加帧动画让他动起来。

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

three.js 自制骨骼动画(一)的更多相关文章

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

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

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

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

  3. three.js之初探骨骼动画

    今后的几篇郭先生主要说说three.js骨骼动画.three.js骨骼动画十分有意思,但是对于初学者来说,学起来要稍微困难一些,官方文档比较少,网上除了用圆柱体的例子就是引用外部模型的,想要熟练使用骨 ...

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

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

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

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

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

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

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

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

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

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

  9. cocos creator 动画编辑器以及骨骼动画的使用

    一.普通动画的设置 1.添加动画组件 a.添加空节点=>添加动画组件 b.新建Clip文件=>打开编辑模式添加动画编辑(并且把添加的clip文件拖动到右边面板的Default Clip 与 ...

随机推荐

  1. 阿里P7岗位面试,面试官问我:为什么HashMap底层树化标准的元素个数是8

    前言 先声明一下,本文有点标题党了,像我这样的菜鸡何德何能去面试阿里的P7岗啊,不过,这确实是阿里p7级岗位的面试题,当然,参加面试的人不是我,而是我部门的一个大佬.他把自己的面试经验分享给了我,也让 ...

  2. 转载之html特殊字符的html,js,css写法汇总

    箭头类 符号 UNICODE 符号 UNICODE HTML JS CSS HTML JS CSS ⇠ &#8672 \u21E0 \21E0 ⇢ &#8674 \u21E2 \21E ...

  3. P3261 [JLOI2015]城池攻占 题解

    题目 小铭铭最近获得了一副新的桌游,游戏中需要用 \(m\) 个骑士攻占 \(n\) 个城池.这 \(n\) 个城池用 \(1\) 到 \(n\) 的整数表示.除 \(1\) 号城池外,城池 \(i\ ...

  4. BUUCTF-Misc-No.2

    比赛信息 比赛地址:Buuctf靶场 [GUET-CTF2019]虚假的压缩包 | SOLVED 解压文件夹,发现2个zip,第一个伪加密,破解后 n=33 e=3 m=0 while m<10 ...

  5. 龙芯开源社区上线.NET主页

    龙芯团队从2019年7 月份开始着手.NET Core的MIPS64支持研发,经过将近一年的研发,在2020年6月18日完成了里程碑性的工作,在github CoreCLR 仓库:https://gi ...

  6. Alink漫谈(十) :线性回归实现 之 数据预处理

    Alink漫谈(十) :线性回归实现 之 数据预处理 目录 Alink漫谈(十) :线性回归实现 之 数据预处理 0x00 摘要 0x01 概念 1.1 线性回归 1.2 优化模型 1.3 损失函数& ...

  7. java IO流 (五) 转换流的使用 以及编码集

    转换流的使用 1.转换流涉及到的类:属于字符流InputStreamReader:将一个字节的输入流转换为字符的输入流解码:字节.字节数组 --->字符数组.字符串 OutputStreamWr ...

  8. 数据可视化实例(十四):带标记的发散型棒棒糖图 (matplotlib,pandas)

    偏差 (Deviation) 带标记的发散型棒棒糖图 (Diverging Lollipop Chart with Markers) 带标记的棒棒糖图通过强调您想要引起注意的任何重要数据点并在图表中适 ...

  9. python 面向对象专题(八):特殊方法 (一)__get__、__set__、__delete__ 描述符(一)

    https://www.cnblogs.com/flashBoxer/p/9771797.html 实现了 __get__.__set__ 或 __delete__ 方法的类是描述符.描述符的用法是, ...

  10. three.js 几何体(三)

    上一篇介绍了几何体的构造体参数,这篇郭先生就接着上一篇说. 1. ExtrudeGeometry挤压几何体 挤压几何体允许我们从一条形状路径中,挤压出一个Geometry.ExtrudeGeometr ...