SOURCE

var __boxBlack = new THREE.MeshBasicMaterial({color:0});

function __box(l) {
	var boxGeo = new THREE.CylinderGeometry(.1,.1, l);
	for(var i =0;i< boxGeo.vertices.length;i++) {
		boxGeo.vertices[i].y+=l/2;
		var gi = boxGeo.vertices[i];
		var tz = gi.z;
		gi.z = gi.y;
		gi.y = -tz;
	}
	var bufferGeo = new THREE.BufferGeometry().fromGeometry(boxGeo);
	return new THREE.Mesh(bufferGeo, __boxBlack);
}


var IKJoint = function(p,pLength, q,qLength,poleVector) {
	this.p = p;
	this.q = q;
	this.pr = pLength;
	this.qr = qLength;
	this.poleVector = poleVector;
	this.line = new THREE.Line3(p,q);
	this.elbow = new THREE.Vector3();
}
IKJoint.prototype.SOLVED  = 1;
IKJoint.prototype.TOO_FAR = 2;
IKJoint.prototype.TOO_CLOSE = 4;

IKJoint.prototype.solve = function() {
	var pq = this.q.clone().sub(this.p);
	this.line.start = this.p;
	this.line.end = this.q;
	var dSquared = pq.lengthSq();
	var dist = Math.sqrt(dSquared);
	// console.log("dist is ", dist, "vs. ",(this.pr+this.qr));

	if(dist>this.pr+this.qr){
		this.outcome = IKJoint.TOO_FAR;
		return;
	}


	if(dist<Math.abs(this.pr-this.qr)) {
		this.outcome = IKJoint.TOO_CLOSE;
		return;
	}

	pq.normalize();

	this.outcome = IKJoint.SOLVED;
	var a =   (this.pr*this.pr - this.qr*this.qr + dSquared) / (2 * dist);
	var h = Math.sqrt(this.pr*this.pr - a*a);

	var pivot = this.p.clone().add(pq.clone().multiplyScalar(a));
	var pqNormal = 
		this.line.closestPointToPoint(this.poleVector, false)
		.sub(this.poleVector)
		.normalize();
	// console.log("pqnormal got to be ", pqNormal);
   this.elbow.lerp(pivot.clone().add(pqNormal.clone().multiplyScalar(-h)),1);
// console.log(this.elbow);
};

IKJoint.prototype.debugDraw = function(scene) {
	if(!this.pStick) {
		this.pStick = __box(this.pr);
		scene.add(this.pStick);
	}
	if(!this.qStick) {
		this.qStick = __box(this.qr);
		scene.add(this.qStick);
	}


	this.pStick.position.lerp(this.elbow,1);
	this.qStick.position.lerp(this.elbow,1);
	this.pStick.lookAt(this.p);
	this.qStick.lookAt(this.q);

};


/*----*/

var Drunkard = function(opts) {
	this.init(opts);
};
var proto = Drunkard.prototype;


proto.init = function(opts) {



	this.hipWidth = 0.1;
	this.hipX = 0.32;
	this.hipY = 0.1;
	this.shoulderX = 0.5;
	this.shoulderY = 0.3;


	this.body = new THREE.Object3D();
	this.hipRoot = 	dot();
	this.hipRoot.position.y = 1.9;


	this.body.add(this.hipRoot);

	this.leftLegRoot = dot();
	this.leftLegRoot.position.set(-this.hipX, -this.hipY,0);
	this.hipRoot.add(this.leftLegRoot);

	this.rightLegRoot = dot();
	this.rightLegRoot.position.set(this.hipX, -this.hipY,0);
	this.hipRoot.add(this.rightLegRoot);


	this.FORWARD = new THREE.Vector3(0,0,100);
	this.BACKWARD = new THREE.Vector3(0,0,-100);


	this.leftFoot = dot();
	this.body.add(this.leftFoot);
	this.leftFoot.position.y = 0.1;
	this.leftFoot.position.x = -this.hipX;
	this.leftFoot.position.z = -1;

	this.rightFoot = dot();
	this.body.add(this.rightFoot);
	this.rightFoot.position.y = 0.1;
	this.rightFoot.position.x = this.hipX;
	this.leftLeg = new IKJoint(new THREE.Vector3(),1, this.leftFoot.position,1,this.FORWARD);


	this.leftHip = __box(this.leftLegRoot.position.length());
	this.rightHip = __box(this.rightLegRoot.position.length());
	this.hipRoot.add(this.leftHip);
	this.leftHip.lookAt(this.leftLegRoot.position);


	this.hipRoot.add(this.rightHip);
	this.rightHip.lookAt(this.rightLegRoot.position);
	this.rightLeg = new IKJoint(new THREE.Vector3(),1, this.rightFoot.position,1,this.FORWARD);


	this.shoulderRoot = dot();
	this.body.add(this.shoulderRoot);
	this.shoulderRoot.position.y = 3.5;

	this.backJoint = new IKJoint(this.shoulderRoot.position,1.1, this.hipRoot.position,0.9, this.BACKWARD);

	this.leftArmRoot = dot();
	this.shoulderRoot.add(this.leftArmRoot);
	this.leftArmRoot.position.set(-this.shoulderX,-this.shoulderY,0);

	this.rightArmRoot = dot();
	this.shoulderRoot.add(this.rightArmRoot);
	this.rightArmRoot.position.set(this.shoulderX,-this.shoulderY,0);

	this.leftWrist = dot();
	this.body.add(this.leftWrist);
	this.leftWrist.position.set(-this.shoulderX*0.5, 4-this.shoulderY-0.3,1.5);

	this.rightWrist = dot();
	this.body.add(this.rightWrist);
	this.rightWrist.position.set(this.shoulderX*0.5, 4-this.shoulderY-0.3,1.5);

	var LOWER_LEFT = new THREE.Vector3(-100,-100,-100);
	var LOWER_RIGHT = new THREE.Vector3(100,-100,-100);

	this.rightArm = new IKJoint(this.rightArmRoot.position, .85, this.rightWrist.position,.85, LOWER_RIGHT);
	this.leftArm  = new IKJoint(this.leftArmRoot.position, .85, this.leftWrist.position,.85, LOWER_LEFT);

	this.leftShoulder = __box(this.leftArmRoot.position.length());
	this.rightShoulder = __box(this.rightArmRoot.position.length());

	this.shoulderRoot.add(this.leftShoulder);
	this.leftShoulder.lookAt(this.leftArmRoot.position);

	this.shoulderRoot.add(this.rightShoulder);
	this.rightShoulder.lookAt(this.rightArmRoot.position);

	this.shoulderRoot.rotation.x = 0.2;

	this.head = dot();
	this.head.scale.multiplyScalar(3);
	this.shoulderRoot.add(this.head);
	this.head.position.y = 0.4;

	this. ikJoints = [this.leftLeg, this.rightLeg,
					this.leftArm, this.rightArm,
					this.backJoint];

	this.endLocators = [this.leftLegRoot,this.rightLegRoot,
					   this.leftArmRoot,this.rightArmRoot];
	this.leftInFlight = false;
	this.leftVY = 0;
	this.leftLeg.foot = this.leftFoot;
	this.leftLeg.vY = 0;

	this.rightLeg.foot = this.rightFoot;
	this.rightLeg.vY = 0;

	this.rightInFlight = false;
   	this.addKnees();
   	this.footRoot = dot();
   	this.body.add(this.footRoot);


};

proto.addKnees = function() {
	this.knees = [];
	for(var i =0;i<this.ikJoints.length;i++) {
		var knee = dot();
		this.body.add(knee);
		this.knees.push(knee);
	}
};


proto.updateLeg = function(leg,other) {
	if(!leg.inFlight) {
		leg.foot.position.x+=this.outwards.x;
		leg.foot.position.z+=this.outwards.z;

		var jointLength = leg.p.distanceTo(leg.foot.position);
		// console.log("the d is ", jointLength);
		var extendedDistance = leg.pr+leg.qr;
		if(jointLength>extendedDistance*0.85) {
			//we should only launch if the foot is closer to the camera than the hip.
			var footDist= camera.position.clone().sub(this.body.localToWorld(leg.foot.position));
			var hipDist= camera.position.clone().sub(this.body.position);
			footDist.y = hipDist.y = 0;

			if(footDist.lengthSq()<hipDist.lengthSq() &&!other.inFlight ) {
				leg.inFlight = true;
				leg.vY = 0.2;
			}

		}
	} else {
		leg.vY -=0.02;
		leg.foot.position.y+=leg.vY;
		// console.log("foot @",leg.foot.position.y.toFixed(2));
		leg.foot.position.x -= 1.7*this.outwards.x;
		leg.foot.position.z -= 1.7*this.outwards.z;
		if(leg.foot.position.y<0) {
			leg.foot.position.y = 0;
			// console.log("landing!");
			leg.inFlight = false;
		}
	}

};

proto.update = function() {


	var tempo = Date.now()/195;


	this.outwards = camera.position.clone().sub(controls.target).normalize().multiplyScalar(.09);

	this.updateLeg(this.leftLeg, this.rightLeg);
	this.updateLeg(this.rightLeg,this.leftLeg);

	var betweenFeet = this.leftFoot.position.clone().lerp(this.rightFoot.position.clone(),  0.5);
	var oy = betweenFeet.y;
	betweenFeet.y = 0;
	this.footRoot.position.lerp(betweenFeet,1.);
	betweenFeet.y = this.hipRoot.position.y;
	// this.hipRoot.position.lerp(betweenFeet,0.01);
	betweenFeet.y = 0;
	this.shoulderRoot.position.lerp(this.hipRoot.position,1.);
	this.shoulderRoot.position.lerp(betweenFeet,-1.);

	//so that should make betweenFeet into the difference between the hip root and between the feet. So we could try
	// this.shoulderRoot.position.lerp(betweenFeet,0.5);
	// if(this.leftLeg.inFlight  ||this.rightLeg.inFlight) {
	// 	this.hipRoot.position.y-=0.004;
	// } else {
	// 	this.hipRoot.position.y+=0.02;

	// }

	this.leftFoot.position.y = .3*Math.abs(Math.cos(tempo/2));
	this.leftFoot.position.z = .7*Math.sin(tempo)+.05;
	this.leftFoot.position.x = .2*Math.sin(tempo)-.1;

	this.rightFoot.position.y = .3*Math.abs(Math.sin(tempo/2));
	this.rightFoot.position.z = .7*-Math.sin(tempo)+.05;
	this.rightFoot.position.x = .2*Math.cos(tempo)+.1;

	this.shoulderRoot.position.x = 0.2*Math.cos(tempo)
	this.shoulderRoot.position.y = 3.8+0.1*Math.cos(tempo*2)
	this.shoulderRoot.rotation.z = 0.4*Math.cos(tempo)
	this.hipRoot.rotation.z = 0.4*Math.sin(tempo);
	this.hipRoot.rotation.y = 0.4*Math.sin(tempo);
	this.hipRoot.position.y = 2+0.1*Math.sin(tempo*2);
	this.hipRoot.position.x = 0.2*Math.sin(tempo);

	this.leftWrist.position.set( -0.6,//-0.2+0.6*Math.sin(Date.now()/300),
									2.6,
								0.5-0.45*Math.sin(tempo));

	this.rightWrist.position.set(0.6,//0.2+0.6*Math.sin(Date.now()/300),
									2.6,
								0.5+0.45*Math.sin(tempo));


	for(var  i =0;i<this.ikJoints.length;i++) {
		if(this.endLocators[i]) {
			bindJointEnd(this.ikJoints[i], this.endLocators[i]);
		}
		this.ikJoints[i].solve();
		this.ikJoints[i].debugDraw(this.body);
		this.knees[i].position.lerp(this.ikJoints[i].elbow,1.);
	}

}

function bindJointEnd(ikJoint, locator) {
	ikJoint.p = locator.parent.localToWorld(locator.position.clone());
}

function dot() {
		return new THREE.Mesh(
			new THREE.SphereBufferGeometry(0.1, 6,6),
			new THREE.MeshBasicMaterial({color:0}));
};

var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xffffff,1);

document.body.style.backgroundColor = "white";
scene.fog = new THREE.FogExp2(0xffffff,0.03);

renderer.setSize(innerWidth,innerHeight);

var camera = new THREE.PerspectiveCamera(45, innerWidth/innerHeight,0.1, 1000);
camera.position.y = 3;
camera.position.z = -10;

camera.lookAt(new THREE.Vector3(0,3,0));
var floor = new THREE.Mesh(
					new THREE.PlaneGeometry(15,15,15,15),
					new THREE.MeshPhongMaterial({

						map:new THREE.Texture(offsetNoise()),
						// emissive:0xffffff,
					}));

var drunkard = new Drunkard();
scene.add(drunkard.body);
var floorCopy = floor.clone();
floor.add(floorCopy);
floorCopy.position.z+=0.05;
floorCopy.material = new THREE.MeshBasicMaterial({
			transparent:true,
			blending:THREE.AdditiveBlending,
			map:new THREE.Texture(blackToWhite()),
});
floorCopy.material.map.needsUpdate = true;
floorCopy.scale.multiplyScalar(1.1);
floor.position.z = 5;
floor.rotation.x = -Math.PI/2;
floor.position.y =0;
floor.material.map.wrapS = floor.material.map.wapT  = THREE.RepeatWrapping;
// floor.material.map.repeat.
floor.material.map.needsUpdate = true;
// floor.material.emissiveMap.needsUpdate = true;


function offsetNoise () {
  var c = document.createElement('canvas');
  var g = c.getContext('2d');
  c.width = c.height = 256;

  var img = g.getImageData(0,0,c.width,c.height);
  for(var i=0;i<img.data.length;i+=4) {
  		var rand = Math.random()*128+39;
  		img.data[i+0] = rand;
  		img.data[i+1] = rand;
  		img.data[i+2] = rand;
  		img.data[i+3] = 255;
  }
  	g.putImageData(img,0,0);

  return c;

}


function blackToWhite () {
  var c = document.createElement('canvas');
  var g = c.getContext('2d');
  c.width = c.height = 128;
  var grad = g.createRadialGradient(64,64,0,64,64, 64*Math.sqrt(2));
  grad.addColorStop(0.4,"#303030");
  grad.addColorStop(0.69,"#ffffff");
  g.fillStyle = grad;
  g.fillRect(0,0,c.width, c.height);
  return c;

}

scene.add(floor);

document.body.appendChild(renderer.domElement);


var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.y = 3;
controls.target.z = 3;
controls.update();




var spotLight = new THREE.SpotLight( 0xffffff,1.6);
scene.add( spotLight );

spotLight.position.set( 0, 15,-7 );


spotLight.castShadow = true;
spotLight.angle = 0.7;
spotLight.penumbra = .2;


spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;

spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 20;
spotLight.shadow.camera.fov = 10;

spotLight.shadow.camera.updateProjectionMatrix();

spotLight.decay = 0;
spotLight.distance = 50;

scene.add(new THREE.AmbientLight(0xffffff,0.0));



var doneTraverse = false;

controls.enableDamping = true;
controls.autoRotate = true;

document.addEventListener('mousedown', onMouseMove);
function onMouseMove (argument) {
		controls.autoRotate = false;
}
floor.rotation.z = Math.PI/2;
function update (time) {

	requestAnimationFrame(update);
	drunkard.update();
	controls.update();

	spotLight.position.x =02*Math.sin(Date.now()/500);

	floor.material.map.offset.x-=0.005;
		// floor.material.emissiveMap.offset.x+=0.01;




	if(!doneTraverse) {

		scene.traverse(function(el){
			if(el.material) {
				el.castShadow = true;
				el.receiveShadow = true;
			}
		});
		doneTraverse = true;
	}


	try {

	renderer.render(scene,camera);
	} catch(err) {}

}

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

update();
canvas {
		position: fixed;
		top: 0;
		left: 0;
		z-index:-10;

	}
console 命令行工具 X clear

                    
>
console