简介

这一节,为了提高自己对SkinnedMesh的理解,查看了官方文档里面的对于这一个功能的介绍。发现,官方竟然是通过手动创建了一个模型。并独自绑定了相关的骨骼,我们可以通过dat.gui插件调整骨骼的缩放,位置和转向来查看骨骼的移动对模型造成的相关影响。

案例实现

案例查看地址:http://www.wjceo.com/blog/threejs/2018-04-22/150.html
为了实现以上案例,我们需要先创建相关的几何体,案例中,我们创建了一个圆柱体的几何体,并且可以通过设置可以设置长度和节数,并设置每一个顶点受到骨骼下标的影响及权重:

var geometry = new THREE.CylinderGeometry(5,                       // 顶部圆柱体的半径5,                       // 底部圆柱体的半径sizing.height,           // 圆柱体的高度8,                       // 圆柱周围的分段面数sizing.segmentCount * 3, // 沿圆柱体高度的面的行数true                     // 圆柱体的末端是打开
);//遍历几何体所有的顶点
for ( var i = 0; i < geometry.vertices.length; i ++ ) {//根据顶点的位置计算出骨骼影响下标和权重var vertex = geometry.vertices[ i ];var y = ( vertex.y + sizing.halfHeight );var skinIndex = Math.floor( y / sizing.segmentHeight );var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );}return geometry;

然后:我们通过实例化new THREE.Bone()创建了一组骨骼,并且设置好相关的父子级别关系:

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;}

最后,我们通过整合,使用geometry创建THREE.SkinnedMesh模型,然后通过那一组骨骼创建THREE.Skeleton骨架,并将骨骼添加到模型里面,模型绑定骨架:

mesh = new THREE.SkinnedMesh( geometry, [material, lineMaterial] );
var skeleton = new THREE.Skeleton( bones ); //创建骨架mesh.add( bones[ 0 ] ); //将骨骼添加到模型里面mesh.bind( skeleton ); //模型绑定骨架

为了能清晰的看清楚骨骼,我们这节也添加了相关的辅助:

//添加骨骼辅助标记
skeletonHelper = new THREE.SkeletonHelper( mesh );
skeletonHelper.material.linewidth = 2;
scene.add( skeletonHelper );

案例代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style type="text/css">html, body {margin: 0;height: 100%;}canvas {display: block;}</style>
</head><body onload="draw();">
</body>
<script src="https://cdn.bootcss.com/three.js/91/three.min.js"></script>
<script src="/lib/js/controls/OrbitControls.js"></script>
<script src="https://cdn.bootcss.com/stats.js/r17/Stats.min.js"></script>
<script src="https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js"></script>
<script src="/lib/js/Detector.js"></script><script>var renderer, camera, scene, gui, light, stats, controls, mesh, skeletonHelper, bones;var state = {animateBones : false};function initRender() {renderer = new THREE.WebGLRenderer({antialias: true});renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth, window.innerHeight);renderer.setClearColor(0xeeeeee);//告诉渲染器需要阴影效果document.body.appendChild(renderer.domElement);}function initCamera() {camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(0, 40, 50);}function initScene() {scene = new THREE.Scene();}//初始化dat.GUI简化试验流程function initGui() {gui = new dat.GUI();var folder = gui.addFolder( "General Options" );folder.add( state, "animateBones" );folder.__controllers[ 0 ].name( "Animate Bones" );folder.add( mesh, "pose" );folder.__controllers[ 1 ].name( ".pose()" );var bones = mesh.skeleton.bones;for ( var i = 0; i < bones.length; i ++ ) {var bone = bones[ i ];folder = gui.addFolder( "Bone " + i );folder.add( bone.position, 'x', - 10 + bone.position.x, 10 + bone.position.x );folder.add( bone.position, 'y', - 10 + bone.position.y, 10 + bone.position.y );folder.add( bone.position, 'z', - 10 + bone.position.z, 10 + bone.position.z );folder.add( bone.rotation, 'x', - Math.PI * 0.5, Math.PI * 0.5 );folder.add( bone.rotation, 'y', - Math.PI * 0.5, Math.PI * 0.5 );folder.add( bone.rotation, 'z', - Math.PI * 0.5, Math.PI * 0.5 );folder.add( bone.scale, 'x', 0, 2 );folder.add( bone.scale, 'y', 0, 2 );folder.add( bone.scale, 'z', 0, 2 );folder.__controllers[ 0 ].name( "position.x" );folder.__controllers[ 1 ].name( "position.y" );folder.__controllers[ 2 ].name( "position.z" );folder.__controllers[ 3 ].name( "rotation.x" );folder.__controllers[ 4 ].name( "rotation.y" );folder.__controllers[ 5 ].name( "rotation.z" );folder.__controllers[ 6 ].name( "scale.x" );folder.__controllers[ 7 ].name( "scale.y" );folder.__controllers[ 8 ].name( "scale.z" );}}function initLight() {scene.add(new THREE.AmbientLight(0x444444));light = new THREE.PointLight(0xffffff);light.position.set(0, 50, 0);//告诉平行光需要开启阴影投射light.castShadow = true;scene.add(light);}function initModel() {//辅助工具var helper = new THREE.AxesHelper(50);scene.add(helper);//制作模型var segmentHeight = 6; //每一节骨骼的的高度var segmentCount = 4; //总节数var height = segmentHeight * segmentCount; //总高度var halfHeight = height * 0.5; //总高度一半的高度var sizing = {segmentHeight : segmentHeight,segmentCount : segmentCount,height : height,halfHeight : halfHeight};var geometry = createGeometry( sizing ); //创建几何体var bones = createBones( sizing ); //创建骨骼mesh = createMesh( geometry, bones ); //创建网格模型//mesh.scale.multiplyScalar( 1 );scene.add( mesh );}//创建集合体function createGeometry ( sizing ) {var geometry = new THREE.CylinderGeometry(5,                       // 顶部圆柱体的半径5,                       // 底部圆柱体的半径sizing.height,           // 圆柱体的高度8,                       // 圆柱周围的分段面数sizing.segmentCount * 3, // 沿圆柱体高度的面的行数true                     // 圆柱体的末端是打开);//添加绘制第二个纹理的面var len = geometry.faces.length;for(var i=0; i < len; i++){var face = geometry.faces[i].clone();face.materialIndex = 1;geometry.faces.push(face);}//将vertexUv报错解决掉var len = geometry.faceVertexUvs[0].length;for(var i=0; i < len; i++){geometry.faceVertexUvs[0].push(geometry.faceVertexUvs[0][i]);}//遍历几何体所有的顶点for ( var i = 0; i < geometry.vertices.length; i ++ ) {//根据顶点的位置计算出骨骼影响下标和权重var vertex = geometry.vertices[ i ];var y = ( vertex.y + sizing.halfHeight );var skinIndex = Math.floor( y / sizing.segmentHeight );var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );}return geometry;}//创建骨骼function 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;}function createMesh ( geometry, bones ) {var material = new THREE.MeshPhongMaterial( {skinning : true,color: 0x156289,emissive: 0x072534,side: THREE.DoubleSide,flatShading: true} );var lineMaterial = new THREE.MeshBasicMaterial({skinning : true,wireframe: true});mesh = new THREE.SkinnedMesh( geometry, [material, lineMaterial] );var skeleton = new THREE.Skeleton( bones ); //创建骨架mesh.add( bones[ 0 ] ); //将骨骼添加到模型里面mesh.bind( skeleton ); //模型绑定骨架//添加骨骼辅助标记skeletonHelper = new THREE.SkeletonHelper( mesh );skeletonHelper.material.linewidth = 2;scene.add( skeletonHelper );return mesh;}//初始化性能插件function initStats() {stats = new Stats();document.body.appendChild(stats.dom);}function initControls() {controls = new THREE.OrbitControls(camera, renderer.domElement);// 如果使用animate方法时,将此函数删除//controls.addEventListener( 'change', render );// 使动画循环使用时阻尼或自转 意思是否有惯性controls.enableDamping = true;//动态阻尼系数 就是鼠标拖拽旋转灵敏度//controls.dampingFactor = 0.25;//是否可以缩放controls.enableZoom = true;//是否自动旋转controls.autoRotate = false;controls.autoRotateSpeed = 0.5;//设置相机距离原点的最远距离controls.minDistance = 1;//设置相机距离原点的最远距离controls.maxDistance = 2000;//是否开启右键拖拽controls.enablePan = true;}function render() {var time = Date.now() * 0.001;//Wiggle the bonesif ( state.animateBones ) {for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {mesh.skeleton.bones[ i ].rotation.z = Math.sin( time ) * 2 / mesh.skeleton.bones.length;}}controls.update();}//窗口变动触发的函数function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}function animate() {//更新控制器render();//更新性能插件stats.update();renderer.render(scene, camera);requestAnimationFrame(animate);}function draw() {//兼容性判断if (!Detector.webgl) Detector.addGetWebGLMessage();initStats();initRender();initScene();initCamera();initLight();initModel();initControls();initGui();animate();window.onresize = onWindowResize;}</script>
</html>

87 Three.js 手动创建SkinnedMesh的骨骼和绑定动画相关推荐

  1. 用SVG和Vanilla JS框架创建一个“星形变心形”的动画效果

    在我写的这篇文章中, 讲述了如何用Vanilla JavaScript使动画顺滑的从一种状态过渡到另一种.最好先看下那篇文章,因为在这篇文章中我们要用到一些那篇文章中讲过的内容.例如例子的演示.各种时 ...

  2. html+js画一颗心形,用SVG和Vanilla JS框架创建一个“星形变心形”的动画效果

    在我写的这篇文章中, 讲述了如何用Vanilla JavaScript使动画顺滑的从一种状态过渡到另一种.最好先看下那篇文章,因为在这篇文章中我们要用到一些那篇文章中讲过的内容.例如例子的演示.各种时 ...

  3. draft.js_如何使用快捷方式在Draft.js中创建有序列表和无序列表

    draft.js by Andrey Semin 通过安德烈·塞米(Andrey Semin) 如何使用快捷方式在Draft.js中创建有序列表和无序列表 (How to create ordered ...

  4. Three.js加载外部模型骨骼动画

    加载外部模型骨骼动画 上节课是通过Threejs程序创建一个骨骼动画然后解析播放,本节课是加载解析一个外部的骨骼动画模型文件. 查看骨骼动画数据 在解析模型骨骼动画之前,先加载外部的三维模型,查看骨骼 ...

  5. 手动创建 vue2 ssr 开发环境

    前言 手动搭建 vue ssr 一直是一些前端开发者的噩梦,因为其中牵扯到很多依赖包之间的配置以及webpack在node中的使用.就拿webpack配置来说,很多前端开发者还是喜欢用webpack- ...

  6. linux怎么创建用户教程,在Linux中如何手动创建一个用户

    1.首先要明白用useradd创建用户的时候会更改添加5个地方的内容 (1)/etc/passwd             //比如创建useradd  111 // [root@localhost ...

  7. oracle之 手动创建 emp 表 与 dept 表

    有时候我们需要通用的实验数据,emp表 与 dept表  但是数据库中有没有. 这时,我们可以手动创建. -- 创建表与数据 CREATE TABLE EMP (EMPNO NUMBER(4) NOT ...

  8. oracle创建数据库后干什么,手动创建Oracle数据库之前因后果

    对于许多数据库管理员来说,Oracle究竟是不是一个难以管理和驾驭的怪兽级数据库,会不会让他们管理生涯饱受折磨?当看到其命令行界面时,很多人会问这么一个问题. 如果你想通过一个应用程序操控使用这个数据 ...

  9. 手动创建一棵二叉树,然后利用前序、中序、后序、层序进行遍历(从创建二叉树到各种方式遍历)(含运行结果)

    手动创建一棵二叉树,然后利用前序.中序.后序.层序进行遍历 import java.util.LinkedList; import java.util.List; import java.util.Q ...

最新文章

  1. C语言之头文件,static与const关键字
  2. easiest approach for improving writing skills for ielts
  3. php程序中报date错误
  4. linux 挂载exfat u盘 yum,centos挂载exfat u盘
  5. 几个年薪百万的下属,爆了~
  6. css3中插入地图,CSS3 地图展开动画
  7. 安装ie11提示计算机安装了更新的版本,离线安装IE11浏览器提示quot;获取更新quot;解决方法 - 191路由网...
  8. HDU-3237-Help Bubu
  9. 【AES】基于FPGA的128位AES加解密系统设计实现
  10. Surface Pro 4 无限重启的解决方法
  11. Mac免费屏保 Brooklyn 苹果logo
  12. java区分无线网卡,无线网卡种类有什么区别
  13. 怎样设置和使用speedceo?简洁浏览器的使用分享
  14. 《计算机网络》第二章作业
  15. display:block含义
  16. SpringBoot框架分层(View层、Controller层、Service层、Mapper层、pojo层)
  17. 【手拉手 带你准备电赛】原来你是这样的触摸屏(电阻触摸屏)
  18. 【C语言期末/实践/大作业】成绩管理系统日程表管理系统
  19. mysql数据库工具Navicat常用快捷键介绍
  20. [渝粤教育] 南京大学 大学生劳动教育 参考 资料

热门文章

  1. OSDI‘22 BEST PAPER“XRP: In-Kernel Storage Functions with eBPF“阅读笔记
  2. 使用Altium Designer进行钢网文件的生成和制作
  3. 请问开SMT钢网需要什么文件?
  4. 农村污水处理工程之污水管网运维要点
  5. 圣经 出埃及记 十诫
  6. Android 是什么
  7. IT运维.服务器常见资质认证
  8. iFonts字体管理工具(神同步PS操作)
  9. Apache Spark SQL 章节六00
  10. 计算广告系列(一)-基本概念整理!