2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 87 Three.js 手动创建SkinnedMesh的骨骼和绑定动画

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

时间:2022-05-15 04:33:18

相关推荐

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

简介

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

案例实现

案例查看地址:/blog/threejs/-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="/three.js/91/three.min.js"></script><script src="/lib/js/controls/OrbitControls.js"></script><script src="/stats.js/r17/Stats.min.js"></script><script src="/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>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。