专栏首页郭先生的博客three.js之初探骨骼动画

three.js之初探骨骼动画

今后的几篇郭先生主要说说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.bones10骨骼的影响有 80%, 受到 skeleton.bones2 的影响是 20%,由于另外两个 skinWeight 的值是 0,因而他们对顶点没有任何影响。

(2) 其材质必须支持蒙皮,并且已经启用了蒙皮,既skinning = true;

(3) 创建骨骼和骨架

2. 骨骼动画的原理

骨骼(Bone)其实就是一个Object3D对象,可以把骨架看成是人体骨架,假如脊柱的根节点,那么大腿就是下一级节点,小腿就是更下一级的节点,如果大腿转动,那么小腿在世界坐标系必然会动,而小腿动,不一定影响大腿。

现在我们假如有一个几何体(这个几何体加上带蒙皮的材质就是我们的腿的网格),想让这个几何体跟着这个骨骼运动,那么这个动画就是骨骼动画,现在我们假设bones0为大腿上端点,bones1为大小腿关节点,bones2为小腿下端点,这里如果我们把腿看成是圆柱体(官方案例就是这样做的),将极大的降低了难度,让heightSegments为2(就是分两段)也就生成了沿高度分布的3层点

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

2. 官网上的骨骼动画

1. 初始化蒙皮网格

//这是生成蒙皮网格的主方法
initBones() {
		//下面是一些会用到的参数
    var segmentHeight = 8; //每段的高度
    var segmentCount = 4;  //段数
    var height = segmentHeight * segmentCount; //总高度
    var halfHeight = height * 0.5; //一般高度

    var sizing = {
        segmentHeight: segmentHeight,
        segmentCount: segmentCount,
        height: height,
        halfHeight: halfHeight
    };

    var geometry = this.createGeometry( sizing ); //这是生成几何体的方法,主要是根据顶点生成对应的skinIndex和skinWeight属性
    var bones = this.createBones( sizing ); //这是生成骨骼的方法
    mesh = this.createMesh( geometry, bones ); //这是生成蒙皮网格的方法

    mesh.scale.multiplyScalar( 1 );
    scene.add( mesh );

    this.render();
    document.getElementById("loading").style.display = "none";  
},

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

createGeometry(sizing) {
		//创建一个圆柱体
    var geometry = new THREE.CylinderBufferGeometry(
        5, // 上面半径
        5, // 下面半径
        sizing.height, // 总高度
        8, // 圆形面分段数
        sizing.segmentCount * 3, // 沿高度的分段数4*3
        true // 无上下面
    );

    var position = geometry.attributes.position; //圆柱体顶点位置集合

    var vertex = new THREE.Vector3(); //创建一个三维向量用于保存顶点坐标

    var skinIndices = []; //顶点索引聚合
    var skinWeights = []; //顶点权重聚合

    for ( var i = 0; i < position.count; i ++ ) { //遍历顶点

        vertex.fromBufferAttribute( position, i ); //依次取出每个点

        var y = ( vertex.y + sizing.halfHeight ); //y保存相对于圆柱体底面的高度值。

				//这两行比较重要
        var skinIndex = Math.floor( y / sizing.segmentHeight ); //高度除以总高度在向下取整,得到当前的skinIndex
        var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight; //当前的y值占该段的百分比

        skinIndices.push( skinIndex, skinIndex + 1, 0, 0 ); //该点关联bone[skinIndex]和bone[skinIndex+1]
        skinWeights.push( 1 - skinWeight, skinWeight, 0, 0 ); //关联bone[skinIndex]的比重为1 - skinWeight,关联bone[skinIndex+1]的比重为skinWeight。
				//举个例子,第一个y值刚好为0。那么skinIndex为0,skinWeight也为0。所以呢该点相关的骨骼索引为0和1,权重分别是1和0,也就是该点只与bone[0]有关。
				//再比如y值为4,那么skinIndex为0,skinWeight也为0.5,所以呢该点相关的骨骼索引为0和1,权重分别是0.5和0.5,也就是该点与bone[0]和bone[1]都相关。其实也很容易理解,因为4恰好在该分段的中间,所以决定于两个骨骼点的状态。

    }

    geometry.setAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( skinIndices, 4 ) ); //几何体中添加skinIndex属性
    geometry.setAttribute( 'skinWeight', new THREE.Float32BufferAttribute( skinWeights, 4 ) ); //几何体中添加skinWeight属性

    return geometry;
},

3. 生成骨骼

createBones(sizing) {
    bones = []; //骨骼数组

    var prevBone = new THREE.Bone(); //根骨骼节点
    bones.push( prevBone ); //数组中添加根骨骼节点
    prevBone.position.y = - sizing.halfHeight; //为根骨骼添加位置

    for ( var i = 0; i < sizing.segmentCount; i ++ ) { //遍历分段

        var bone = new THREE.Bone(); //创建骨骼节点
        bone.position.y = sizing.segmentHeight; //为骨骼节点添加本地位置 虽然本地设置的位置都是一样的,但是由于这些骨骼都是父子关系,所以在世界坐标系上位置不同
        bones.push( bone ); //数组中继续添加骨骼
        prevBone.add( bone ); //根骨骼添加当前骨骼
        prevBone = bone; //再将当前骨骼赋值给根骨骼

    }
    return bones;
},

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

createMesh(geometry, bones) {
		//创建一个带蒙皮的材质
    var material = new THREE.MeshPhongMaterial( {
        skinning: true, //重点
        color: 0x156289,
        emissive: 0x072534,
        side: THREE.DoubleSide,
        flatShading: true
    } );

    var mesh = new THREE.SkinnedMesh( geometry,	material ); //创建蒙皮网格
    var skeleton = new THREE.Skeleton( bones ); //创建骨架

    mesh.add( bones[ 0 ] ); //网格添加根骨骼节点(此例bones[0]为根节点)

    mesh.bind( skeleton ); //网格绑定骨架

    skeletonHelper = new THREE.SkeletonHelper( mesh ); //创建骨骼显示助手
    skeletonHelper.material.linewidth = 2;
    scene.add( skeletonHelper );

    return mesh;
},

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

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

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

    郭先生的博客
  • three.js 自制骨骼动画(二)

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

    郭先生的博客
  • 【带着canvas去流浪(11)】Three.js入门学习笔记

    官方文档中的新手示例过于简单,所以本节对Three.js中的概念进行一些补充描述:

    大史不说话
  • 骨骼动画系统Cal3D

    在群里看到有人把它用在了商业游戏中, 就很好奇为什么用它. 去网站上看了一下, 觉得许多特性很不错, 像动作融合, LOD之类的 试着用DXUT参考minivi...

    逍遥剑客
  • 《Honey Select》捏人剖析

    关于游戏中的捏人系统, 很少有资料提到怎么做, 印象中只有《天涯明月刀》分享过. 前段时间关注了个VR资源分享的公众号, 经常推送HS的捏人作品, 所以才引发了...

    逍遥剑客
  • SceneKit_入门13_骨骼动画

    1.创建工程(略) 2.加载场景文件(略) 3.添加框架SceneKit/Scenekit.h 4.创建场景资源对象

    酷走天涯
  • 马斯克的“半机械人”还没出现,这位日本少年却已经可以让你变身“半机械人”了

    大数据文摘
  • Web AR 技术调研笔记

    去年年末集中学习了一下已经存在的web ar 相关的项目和技术,并整理和试用了一下各项目的Demo,为了备忘记录了一份笔记。

    邹成卓
  • 游戏基础课程-游戏开发流程

    1、前期预案 项目发起,提出项目初步开发意向 2、初步市场规划 指定计划,组织相关人员,并进行策划管理,财务预算,市场调研 3、项目前期创意 剧本创作...

    孙寅
  • 短视频特效“耍花招”:线上投篮、摆摊,让画中人摇摆,浅谈腾讯微视的AR基建

    魔性的背景音乐、酷炫的AR特效、多元的内容题材,让大众欲罢不能的短视频App正在成为内容生产和传播主要渠道之一。

    AI科技大本营
  • Three.js camera初探——转场动画实现

    three.js是用javascript写的基于webGL的第三方3D库,通过它可以在网页中进行3D建模,结合上TweenMax.js动画库,在网页中实现3D动...

    郭诗雅
  • Unity动画系统

    Animator 1.Animator Controller 2.Avator对角色中骨骼进行分析识别,并与Mecanim中骨骼进行对比。

    祝你万事顺利
  • 纯CSS实现简单骨骼动画

    某天设计师来找我说,“这个心愿牌傻傻地挂在那不好看,加个动效呗,就左右摆动一下就行,很简单的!”,我一想,行呀,提升用户视觉体验,开搞。

    Javanx
  • 1.9K star量,解救无聊网友,小姐姐的「动画师」项目可一键生成个人角色

    最近,一个名为「Pose Animator」的项目人气暴增,打开以后,我们发现这又是一个能让人自娱自乐,并且丝毫察觉不到时间流逝的神奇工具。

    机器之心
  • 当你无聊时,可以玩玩 GitHub 上这个开源项目

    最近,一个名为「Pose Animator」的项目人气暴增,打开以后,我们发现这又是一个能让人自娱自乐,并且丝毫察觉不到时间流逝的神奇工具。

    崔庆才
  • 操纵加鲁鲁兽的机会来了,SIGGRAPH论文提出RigNet帮动画师做骨架绑定

    什么是动画骨架绑定呢?基于 3D 蒙皮创建骨骼。动画模型中的关节就像现实中人的关节一样,两个关节组成一段骨骼,几段骨骼组成一个骨架。绑定就是把模型绑定到骨骼上边...

    机器之心
  • UE4中程序驱动的LookAt动画

    逍遥剑客
  • VR中的动画就是这么玩哒

    导读 大家是不是觉得VR中的动画特别神奇,其实它是基于Unity中的动画系统Mecanim实现的,Unity在5.0之后,Mecanim动画控制越来越强大好用...

    腾讯移动品质中心TMQ
  • AI 赋能游戏工业化,网易互娱AI Lab动捕去噪新方法入选 SIGGRAPH 2021

    当游戏行业仍在聚焦探讨如何让 AI 真正落地、协助游戏的工业化制作时,网易互娱 AI Lab 已基于游戏研发制作中的痛点交出了一份令人惊艳的答卷。

    AI科技评论

扫码关注云+社区

领取腾讯云代金券