three.js动画的基本骨架

时间:2017-02-22 20:58:33

标签: javascript animation three.js

我的目标是拥有一个像3骨骼骨架的东西,每个关节上只有一个球体,例如,肩膀上有一个球体的手臂,一个在肘部,一个在手腕上。

我找到的所有东西都使用了一些JSON加载器,它可以完成构建骨架的所有工作。复制Three.js的文档,我制作了这段代码,它似乎在(0,0,0)点显示3个球体。

//all this lies in my init() function
var bones = [];
var shoulder = new THREE.Bone();
var elbow = new THREE.Bone();
var wrist = new THREE.Bone();

//link the bones
shoulder.add(elbow);
elbow.add(wrist);

//set the default position of the bones
shoulder.position.set(1, 1, 1);
elbow.position.set(1, 1, 2);
wrist.position.set(1, 1, 3);

//put all the bones in an array for the skeleton
bones.push(shoulder);
bones.push(elbow);
bones.push(wrist);

//this one is a global variable in my script
skel = new THREE.Skeleton(bones);

var m = new THREE.MeshPhongMaterial( { color: 0xffff00, emissive: 0x072534, shading: THREE.SmoothShading} );

//create the geometry for the spheres
var g1 = new THREE.SphereGeometry(0.2, 100, 100);
var g2 = new THREE.SphereGeometry(0.2, 100, 100);
var g3 = new THREE.SphereGeometry(0.2, 100, 100);

for (var i = 0; i < g1.vertices.length; i++){
    // put the indices of the bones and the associated weights 
    //on each vertex of the geometry
    g1.skinIndices[i] = new THREE.Vector4(0, 0, 0, 0);
    g1.skinWeights[i] = new THREE.Vector4(1, 0, 0, 0);
    g2.skinIndices[i] = new THREE.Vector4(1, 0, 0, 0);
    g2.skinWeights[i] = new THREE.Vector4(1, 0, 0, 0);
    g3.skinIndices[i] = new THREE.Vector4(2, 0, 0, 0);
    g3.skinWeights[i] = new THREE.Vector4(1, 0, 0, 0);
}

//create the meshes
var s1 = new THREE.SkinnedMesh(g1, m);
var s2 = new THREE.SkinnedMesh(g2, m);
var s3 = new THREE.SkinnedMesh(g3, m);

//bind the meshes with the bones.
//I guesse this is the problematic part
s1.add(skel.bones[0]);
s1.bind(skel);
s2.add(skel.bones[1]);
s2.bind(skel);
s3.add(skel.bones[2]);
s3.bind(skel);

scene.add(s1);
scene.add(s2);
scene.add(s3);

我有一个render()和一个animate()函数,可以显示一些简单的几何体。 (我没有把它们放在这里以避免显示太多不相关的代码)

实际上,我不知道s1.add(skel.bones[0])s1.bind(skel)到底做了什么:我在一个例子中看到了这个并尝试复制我看到的内容,但它没有做我想要的事情去做。我只是希望将每个球体放在不同的位置,并且能够将骨架用于动画目的。

1 个答案:

答案 0 :(得分:1)

注意,英语不是我的自然语言!

我同意,我找不到任何真正的基础教程!这就是我为什么要进行实验的原因。事情有点困难。有一些必要的步骤,“s1.add(skel.bones [0])和s1.bind(skel)真正做什么”只是其中之一。

首先,您必须创建彼此依赖的骨骼。然后,您必须根据特定几何计算顶点对骨骼的依赖关系。每个顶点最多可以包含四个骨骼。在示例中,我只计算了两个骨骼。

现在从骨骼创建一个骨架。下一步是骨骼,骨架和网格的连接: skeleton = new THREE.Skeleton( bones );
mesh.add( bones[ 0 ] ); // add the first bone to the mesh
mesh.bind( skeleton ); // connect the skeleton

要查看骨架使用情况: skeletonHelper = new THREE.SkeletonHelper( mesh );

示例中的骨骼是使用函数createBones(positionY, height, boneCount)

创建的

查找不同的函数skinIndexWeigtLatheBody()skinIndexWeightCylinder(geometry)。气缸和车床比球体更容易计算,因为顶点更系统,更简单。

最后用骨头制作动画。 mesh.skeleton.bones.rotation.x = ...

剧本:

document.getElementById('move').checked = false;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 20000 );
camera.position.set(-200,100,200);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xeeeeee, 1 );	
container = document.createElement('div');
document.body.appendChild(container);
container.appendChild(renderer.domElement); 	
orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = true;
light1 = new THREE.PointLight(0xffffff,1,0); 
light1.position.set( -100, 300, 1000 ); 	
scene.add(light1);		
clock  = new THREE.Clock(true);
geometryNeck =  new THREE.SphereGeometry(10, 6, 6);  
rSegmentCount = 8; // radial segments 
bodyHeight =   80; // body: LatheGeometry
boneBodyCount 		=  3;		
boneBodySegCount 	=  3;      
bodySegHeightCount	=  boneBodySegCount * boneBodyCount; // segments total
boneBodyHeight		=  bodyHeight / boneBodyCount ;	
outlineBody = [			
 [0.01,-bodyHeight ],[20,-78],[35,-74],[42,-65],[45,-55],[44,-45],[30,-27],[10,0] //  from -bodyHeight to 0
];	
pointsBody = [];   // Vector2 points  x,y
for ( var i = 0; i < outlineBody.length ; i++ ) {
   pointsBody.push(new THREE.Vector2(outlineBody[i][0], outlineBody[i][1]))
}
geometryBody = new THREE.LatheGeometry( pointsBody, rSegmentCount );  
skinIndexWeigtLatheBody(); 	// function
//---
limbRadius	    	=  4;
limbHeight    		= 60;
boneLimbCount		=  3; 
boneLimbSegCount	=  1;				
limbRadSegCount		=  6; // or 0.5*rSegmentCount; 	
limbHeightSegCount	=  boneLimbSegCount*boneLimbCount;
boneLimbHeight		= limbHeight/ boneLimbCount ;	
limbHalfHeight		= limbHeight * 0.5;	
openEnded     = false; 			
geometryLimbs = [];
for (var i=0; i<2;i++){  // 2 limbs
  geometryLimbs[i] = new THREE.CylinderGeometry(limbRadius,limbRadius,limbHeight,limbRadSegCount,limbHeightSegCount,openEnded);
}
bonesBody = createBones( 0 , -boneBodyHeight, boneBodyCount); // function
bonesLimbs = [];
for (var i=0; i<2; i++){
              // function
 bonesLimbs[i] = createBones(-limbHalfHeight, boneLimbHeight, boneLimbCount); 
}
material  = new THREE.MeshPhongMaterial({ color: 0x896215, emissive: 0xa96415, wireframe: true, skinning: true});	
meshNeck  = new THREE.Mesh(geometryNeck, material);
meshBody  = new THREE.SkinnedMesh(geometryBody, material );
meshBody.add( meshNeck );	
geometrysLimbs = [];
meshesLimbs = [];
for (var i=0; i<2; i++){
	skinIndexWeightCylinder(geometryLimbs[i]);  	// function
	meshesLimbs[i] =  new THREE.SkinnedMesh(geometryLimbs[i], material);
}
meshesLimbs[0].rotation.z =  1.57;   				// arm right 
meshesLimbs[0].position.x = -limbHalfHeight;
meshesLimbs[1].rotation.z =  -1.57;  				// arm left 
meshesLimbs[1].position.x =  limbHalfHeight;	
skeletonBody = new THREE.Skeleton( bonesBody );		
meshBody.add( bonesBody[ 0 ] );						
meshBody.bind( skeletonBody );						
scene.add( meshBody );
skeletonHelperBody = new THREE.SkeletonHelper( meshBody );  
scene.add( skeletonHelperBody );
skeletonsLimbs = [];
skeletonHelperLimbs = [];
for (var i=0; i<2; i++){
	skeletonsLimbs[i] = new THREE.Skeleton( bonesLimbs[i] );	            
	meshesLimbs[i].add( bonesLimbs[i][0] );								
	meshesLimbs[i].bind( skeletonsLimbs[i] );								
	meshBody.add( meshesLimbs[i] );
	skeletonHelperLimbs[i] = new THREE.SkeletonHelper( meshesLimbs[i] );  
	scene.add( skeletonHelperLimbs[i] );
}
//........................................................................
animate();
//........................................................................
function skinIndexWeigtLatheBody(){
	for ( var i =0; i<geometryBody.vertices.length; i++ ) {   
		vertexY =  geometryBody.vertices[ i ].y ;           									// only  y dependet
		skinIndex = boneBodyCount-1 - Math.floor((i % bodySegHeightCount)/ boneBodySegCount) ;  // skin-index,  bone 0 top 	   
		skinWeight = ( (bodyHeight - vertexY)  %  boneBodyHeight ) / boneBodyHeight ;        	// weight
		geometryBody.skinIndices.push( new THREE.Vector4(    skinIndex, skinIndex+1, 0, 0 ) );  // allocation (2 from max. 4 bones)
		geometryBody.skinWeights.push( new THREE.Vector4( 1-skinWeight, skinWeight , 0, 0 ) );	
	}
}
function skinIndexWeightCylinder(geometry){
	for ( var i = 0; i < geometry.vertices.length; i ++ ) {        
		vertexY =  geometry.vertices[ i ].y  +  limbHalfHeight;				// only  y dependet
		skinIndex = Math.floor( vertexY  / boneLimbHeight );            	// bone 0: bottom  
		skinWeight = ( vertexY  % boneLimbHeight) /  boneLimbHeight;        // weight
		geometry.skinIndices.push( new THREE.Vector4(    skinIndex, skinIndex+1, 0, 0 ) );  // allocation (2 from max. 4 bones)
		geometry.skinWeights.push( new THREE.Vector4( 1-skinWeight, skinWeight , 0, 0 ) );
	}
}
function createBones(positionY, height, boneCount){
    bones = [];                   // base: bone 0
	basicBone = new THREE.Bone(); // base bone, length 0, not visible
	bones.push( basicBone );          
	basicBone.position.y = positionY; 
	prevBone = basicBone; 	    	  // previous bone for further
	for ( var i = 1; i < boneCount+1 ; i ++ ) {
		bone = prevBone.clone();             	
		bone.position.y = height;  // at the right distance ...
		bones.push( bone );		   //   lay down
		prevBone.add( bone ); 	   //  ... each to the previous bone
		prevBone = bone;		   // new previous bone       
	}
	return bones;
}
function animate() {
	requestAnimationFrame( animate );
	var time =  clock.getElapsedTime();  	
	if ( document.getElementById("move").checked) {  // HTML: <input type="checkbox" id="move"> move         				
		for ( var i = 1; i < meshBody.skeleton.bones.length - 2; i ++ ) {		
			meshBody.skeleton.bones[ i ].rotation.z = 0.5*Math.sin( 1.8*time ) / meshBody.skeleton.bones.length;			
		}		
		for ( var i = 1; i < boneLimbCount+1; i++ ) {
			meshesLimbs[0].skeleton.bones[ i ].rotation.x = 0.6*Math.cos( 1.2*time ) / boneLimbCount;	
			meshesLimbs[0].skeleton.bones[ i ].rotation.z = 0.6*Math.sin( 1.2*time ) / boneLimbCount;	
			meshesLimbs[1].skeleton.bones[ i ].rotation.x = 0.6*Math.cos( 1.2*time ) / boneLimbCount;	
			meshesLimbs[1].skeleton.bones[ i ].rotation.z = 0.6*Math.sin( 3.14+1.2*time ) / boneLimbCount;
		}
		meshNeck.rotation.x = -0.20*(0.8+Math.sin( -1.57+1.2*time )); 			
		meshNeck.rotation.y = -0.15*(0.6+Math.sin( -1.57+1.9*time ));
		meshBody.rotation.x = 0.2;	
		skeletonHelperBody.update();
		for ( var j = 0; j < 2; j++ ){ 
			skeletonHelperLimbs[j].update(); 
		}	
	} 
	renderer.render( scene, camera );
}
<input type="checkbox" id="move">  move 

<script src="../js/three.min.84.js"></script>
<script src="../js/OrbitControls.js"></script>

示例位于http://threejs.hofk.de/ Skeleton Basic。

“Hummel Mara”(大黄蜂玛拉)展示了一个扩展的例子。一个带骨头的大黄蜂!它基于之前的骨架例子。 //评论是用德语写的,但是用英语命名的变量。

Knochen - bone,Skelet - skeleton,Knoten - vertex,( - node ...),Ziffer - number,Tafel - table,bewegen - move ...带翻译德语/英语,如dict.cc或google tanslater - 我也是。

顺便说一下,不要在开头使用100个球段。