【技术调研】三维(5)-ThreeJs物理运动及案例

物理运动

threejs本身不具备模拟物理的效果,需要借助cannonjs来实现模拟物理效果。

可以理解为threejs构建看得见的物体,cannonjs构建看不见的物理物体,将两者进行关联。

cannonjs

官网 https://schteppe.github.io/cannon.js/

文档 https://schteppe.github.io/cannon.js/docs/

物体运动

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<title>My first three.js app</title>
	<style>
		body {
			margin: 0;
		}
	</style>
</head>

<body>
	<script type="module">
		import * as THREE from "three";
		import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
		import * as CANNON from 'cannon-es'
		import { Mesh } from "three";
		import { Sphere, Vec3 } from "cannon-es";
		 
		/**
		 * Debug
		 */
		const hitSound=new Audio("sounds/hit.mp3")
		const playHitSound=(collision)=>{
		  if(collision.contact.getImpactVelocityAlongNormal()>1.5){
		    hitSound.currentTime=0;
		    hitSound.volume=Math.random()
		    hitSound.play();
		  }
		}
		/**
		 * Base
		 */
		// Canvas
		const textureLoader=new THREE.TextureLoader()
		let objsToUpdate=[]
		// Scene
		const scene = new THREE.Scene();
		const world=new CANNON.World()
		world.gravity.set(0,-9.82,0)    //设置重力方向
		const defaultMat=new CANNON.Material('default')
		const defaultContactMaterial=new CANNON.ContactMaterial(defaultMat,defaultMat,{
		  friction:0.1,
		  restitution:0.6
		})
		world.addContactMaterial(defaultContactMaterial)
		 
		world.broadphase=new CANNON.SAPBroadphase(world)
		world.allowSleep=true
		const floorBody=new CANNON.Body({
		  mass:0,
		  shape:new CANNON.Plane(),
		  material:defaultContactMaterial
		})
		floorBody.quaternion.setFromAxisAngle(
		  new CANNON.Vec3(-1,0,0),
		  Math.PI*0.5
		  
		)
		world.addBody(floorBody)
		/**
		 * Light
		 */
		 const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
		 scene.add(ambientLight);
		 
		 const directionalLight = new THREE.DirectionalLight(0xffffff, 0.2);
		 directionalLight.castShadow = true;
		 directionalLight.shadow.mapSize.set(1024, 1024);
		 directionalLight.shadow.camera.far = 15;
		 directionalLight.shadow.camera.left = -7;
		 directionalLight.shadow.camera.top = 7;
		 directionalLight.shadow.camera.right = 7;
		 directionalLight.shadow.camera.bottom = -7;
		 directionalLight.position.set(5, 5, 5);
		 scene.add(directionalLight);
		/**
		 * Objects
		 */
		 const sphere=new THREE.SphereGeometry(1,20,20)
		 const sphereMateral=new THREE.MeshStandardMaterial({
		   roughness:0.4,
		   metalness:0.3,
		 })
		 const box=new THREE.BoxGeometry(1,1,1)
		 const boxMaterial=new THREE.MeshStandardMaterial({
		  roughness:0.4,
		  metalness:0.3,
		})
		const createBox=(width,height,depth,position)=>{
		  const mesh=new Mesh(box,boxMaterial)
		   mesh.scale.set(width,height,depth)
		  mesh.castShadow=true
		  mesh.position.copy(position)
		  const body=new CANNON.Body({
		    mass:1,
		    position:new Vec3().copy(position),
		    shape:new CANNON.Box(new CANNON.Vec3(width,height,depth)),
		    material:defaultContactMaterial
		  })
		  body.addEventListener("collide",playHitSound)
		 
		  objsToUpdate.push({mesh,body})
		  scene.add(mesh)
		  world.addBody(body)
		}
		const createSphere=(r,ps)=>{
		  const mesh=new Mesh(sphere,sphereMateral)
		  mesh.scale.set(r,r,r)
		  mesh.castShadow=true
		  mesh.position.copy(ps)
		  scene.add(mesh)
		 
		  const body=new CANNON.Body({
		    mass:1,
		    position:new Vec3().copy(ps),
		    shape:new Sphere(r),
		    material:defaultContactMaterial
		  })
		  objsToUpdate.push({mesh,body})
		  world.addBody(body)
		}
		createSphere(0.5,{x:0,y:3,z:0})
		const floor=new Mesh(
		  new THREE.PlaneGeometry(10,10),
		  new THREE.MeshStandardMaterial({
		    color: "#777777",
		    metalness: 0.3,
		    roughness: 0.4,
		  })
		)
		floor.castShadow=true
		floor.receiveShadow = true;
		floor.rotation.x = -Math.PI * 0.5;
		scene.add(floor)
		/**
		 * Sizes
		 */
		const sizes = {
		  width: window.innerWidth,
		  height: window.innerHeight,
		};
		 
		window.addEventListener("resize", () => {
		  // Update sizes
		  sizes.width = window.innerWidth;
		  sizes.height = window.innerHeight;
		 
		  // Update camera
		  camera.aspect = sizes.width / sizes.height;
		  camera.updateProjectionMatrix();
		 
		  // Update renderer
		  renderer.setSize(sizes.width, sizes.height);
		  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
		});
		 
		/**
		 * Camera
		 */
		// Base camera
		const camera = new THREE.PerspectiveCamera(
		  75,
		  sizes.width / sizes.height,
		  0.1,
		  100
		);
		camera.position.set(-3, 3, 3);
		scene.add(camera);
		 

		/**
		 * Renderer
		 */
		const renderer = new THREE.WebGLRenderer({
			antialias: true
		})
		renderer.setSize(sizes.width, sizes.height);
		renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
		 
		renderer.shadowMap.enabled=true 
		// sphereBody.applyLocalForce(new CANNON.Vec3(150, 0, 0));
		 document.body.appendChild(renderer.domElement);
		 
		 // Controls
		 const controls = new OrbitControls(camera, renderer.domElement);
		 controls.enableDamping = true;
		  
		  
		const clock = new THREE.Clock();
		const oldTime=0;
		const tick = () => {
		  const elapsedTime = clock.getElapsedTime();
		  const deltaTime=elapsedTime-oldTime;
		 
		  objsToUpdate.forEach(obj=>{
		    obj.mesh.quaternion.copy(obj.body.quaternion)
		    obj.mesh.position.copy(obj.body.position)
		  })
		  world.step(1/60,deltaTime,3)
		 
		  // Update controls
		  controls.update();
		 
		  // Render
		  renderer.render(scene, camera);
		 
		  // Call tick again on the next frame
		  window.requestAnimationFrame(tick);
		};
		 
		tick();
		 
		// const guiObj={
		  
		// }
		// guiObj.createSphere=()=>{
		//   createSphere(Math.random()*0.5,{x:(Math.random()-0.5)*3,y:3,z:(Math.random()-0.5)*3})
		// }
		// guiObj.createBox=()=>{
		//    createBox(Math.random()*0.5,Math.random()*0.5,Math.random()*0.5,{x:(Math.random()-0.5)*3,y:3,z:(Math.random()-0.5)*3})
		// }
		// guiObj.restCanvas=()=>{
		//   objsToUpdate.forEach(obj=>{
		//     scene.remove(obj.mesh)
		//     world.removeBody(obj.body)
		//     obj.body.removeEventListener("collide",()=>playHitSound())
		 
		//   })
		// }
		// gui.add(guiObj,'createSphere')
		// gui.add(guiObj,'createBox')
		// gui.add(guiObj,'restCanvas')
	</script>
</body>

</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值