6-1 初识Points与点材质
-
创建一个球,展示方式为网格,颜色为红色
const sphereGeometry = new THREE.SphereGeometry(3, 20, 20); const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true, }); const sphere = new THREE.Mesh(sphereGeometry, material); scene.add(sphere); -
创建点材质
const sphereGeometry = new THREE.SphereGeometry(3, 20, 20); // const material = new THREE.MeshBasicMaterial({ // color: 0xff0000, // wireframe: true, // }); // const sphere = new THREE.Mesh(sphereGeometry, material); // scene.add(sphere); // 设置点材质(大小) const pointsMaterial = new THREE.PointsMaterial(); pointsMaterial.size = 0.1; // 创建点堆物体 const points = new THREE.Points(sphereGeometry, pointsMaterial); scene.add(points);
6-2 深度解析点材质属性
-
设置点材质颜色
pointsMaterial.color.set(0xfff000);
-
设置是否按相机深度衰减:默认true,一般不进行设置(上图中的方块近大远小,设置为false每个点会一样大)
pointsMaterial.sizeAttenuation = false;
-
为点材质设置纹理,点材质设置纹理的时候是对整个物体进行纹理设置,而不是物体上面的每一个点设置
// 注掉相机深度衰减 // pointsMaterial.sizeAttenuation = false; // 载入纹理 const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./textures/particles/1.png"); pointsMaterial.map = texture; pointsMaterial.alphaMap = texture; pointsMaterial.transparent = true;threejs版本:0.152.2 和 0.166.0
- 设置map时,材质给整个物体区域设置纹理
threejs版本:0.137.5
- 设置map时,材质给整个物体区域内的每一个点设置纹理
-
设置点材质后,材质图案有黑边的时候,前面的点会挡到后面的点
可以通过设置透明度贴图来解决
pointsMaterial.alphaMap = texture; pointsMaterial.transparent = true;![1721877639548.png]
-
depthWrite:Boolean 渲染此材质是否对缓冲区有任何影响。默认为true。
pointsMaterial.depthWrite = false;![1721877937698.png]
-
blending:Blending
-
使用此材质显示对象时要使用何种混合
-
必须将其设置为CustomBlending才能使用自定义blendSrc, blendDst 或者 [page:Constant blendEquation]。 混合模式所有可能的取值请参阅constants。默认值为NormalBlending。
-
混合模式
THREE.NoBlending // 无混合 THREE.NormalBlending // 正常混合 THREE.AdditiveBlending // 叠加混合 THREE.SubtractiveBlending // 减法混合 THREE.MultiplyBlending // 乘法混合 THREE.CustomBlending // 自定义混合
-
6-3 应用定点着色器打造绚丽多彩的星空
-
创建星空效果
// 不要之前的球体范围了,保留材质等的设置 // 创建缓冲物体 const particlesGeometry = new THREE.BufferGeometry(); //设置5000个点 const count = 5000; // 设置缓冲区数组 const positions = new Float32Array(count * 3); // 设置顶点 for (let i = 0; i < count * 3; i++) { positions[i] = (Math.random() - 0.5) * 30; } // 设置各个点的位置 particlesGeometry.setAttribute( "position", new THREE.BufferAttribute(positions, 3) );
-
创建五彩的星空效果
// 设置每一个顶点的颜色 const colors = new Float32Array(count * 3); // 设置顶点 for (let i = 0; i < count * 3; i++) { positions[i] = (Math.random() - 0.5) * 30; colors[i] = Math.random(); // 设置颜色值 Math.random() 默认0-1,对于颜色来说就是0-255 } particlesGeometry.setAttribute( "position", new THREE.BufferAttribute(positions, 3) ); // 设置顶点颜色 particlesGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); /** * 以上设置不会启效果,需要启用顶点颜色设置 */ pointsMaterial.vertexColors = true;
6-4 通过封装与相机裁剪实现漫天飞舞的雪花
-
素材网站:
- iconfont
- 爱给网
-
实现漫天飞舞的雪花
![snow.png]
// 将纹理替换为雪花的纹理 const texture = textureLoader.load("./textures/particles/snow.png"); // 将顶点的颜色值设置为1 for (let i = 0; i < count * 3; i++) { positions[i] = (Math.random() - 0.5) * 30; colors[i] = 1; } // 将之前设置的黄颜色注释掉 // pointsMaterial.color.set(0xfff000); // 在渲染函数中设置旋转值 function render() { let time = clock.getElapsedTime(); points.rotation.x = time * 0.05; controls.update(); renderer.render(scene, camera); // 渲染下一帧的时候调用render函数 requestAnimationFrame(render); }
-
将创建雪花封装成函数
// 创建物体 function createPoints(url, size = 0.5, color) { // 创建物体 const particlesGeometry = new THREE.BufferGeometry(); const count = 10000; // 设置缓冲区数组 const positions = new Float32Array(count * 3); // 设置每一个顶点的颜色 const colors = new Float32Array(count * 3); // 设置顶点 for (let i = 0; i < count * 3; i++) { positions[i] = (Math.random() - 0.5) * 30; if (color) { colors[i] = color; } else { colors[i] = Math.random(); } } particlesGeometry.setAttribute( "position", new THREE.BufferAttribute(positions, 3) ); particlesGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); // 设置点材质(大小) const pointsMaterial = new THREE.PointsMaterial(); pointsMaterial.size = size; // 载入纹理 const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load(`./textures/particles/${url}.png`); pointsMaterial.map = texture; pointsMaterial.alphaMap = texture; pointsMaterial.transparent = true; pointsMaterial.depthWrite = false; pointsMaterial.blending = THREE.AdditiveBlending; // 启用顶点颜色设置 pointsMaterial.vertexColors = true; // 创建点堆物体 const points = new THREE.Points(particlesGeometry, pointsMaterial); scene.add(points); return points; } // 创建三个不同的粒子 const pointsSnow = createPoints("snow", 0.05, 1); const pointsStart = createPoints("9", 0.1, 1); const points08 = createPoints("8", 0.1, 1); // 设置三个不同粒子的运动方向 function render() { let time = clock.getElapsedTime(); pointsSnow.rotation.x = time * 0.05; pointsStart.rotation.x = time * 0.05; pointsStart.rotation.y = time * 0.01; points08.rotation.x = time * 0.01; points08.rotation.y = time * 0.01; controls.update(); renderer.render(scene, camera); // 渲染下一帧的时候调用render函数 requestAnimationFrame(render); }上述方式设置完后,可以看到有的粒子往相反的方向(上)运动,这个时候可以设置相机的近端面和远端面
// 创建相机 const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 10 // 将远端面设置的和相机的位置一样,就可以看到粒子都是往下运动的 ); // 设置相机位置 camera.position.set(0, 0, 10);![1721895058035.png]
6-5 运用数学知识打造复杂形状臂旋星系01
-
目标:给x轴上面设置点
// 创建纹理加载器并加载纹理 const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load("./textures/particles/1.png"); // 设置参数 const params = { count: 100, size: 0.1, radius: 5, branch: 3, color: "#ffffff", }; let geometry = null; let material = null; let points = null; // 生成点的函数 const generateGalaxy = () => { // 生成顶点 geometry = new THREE.BufferGeometry(); // 随机生成位置 const positions = new Float32Array(params.count * 3); // 设置顶点颜色 const colors = new Float32Array(params.count * 3); // 循环生成顶点(count个顶点,只给x设置值,y和z应该是0) for (let i = 0; i < params.count; i++) { const current = i * 3; positions[current] = Math.random() * params.radius; positions[current + 1] = 0; positions[current + 2] = 0; } geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3)); // 设置点参数 material = new THREE.PointsMaterial({ color: new THREE.Color(params.color), size: params.size, sizeAttenuation: true, depthWrite: false, blending: THREE.AdditiveBlending, map: texture, alphaMap: texture, transparent: true, // vertexColors: true, }); points = new THREE.Points(geometry, material); scene.add(points); }; generateGalaxy();
6-6 运用数学知识打造复杂形状臂旋星系02
-
目标:总共设置三个分支,在三个分支都生成点
// 循环生成顶点 for (let i = 0; i < params.count; i++) { // 当前的点应该在哪一条分支的角度上(分支值 * 两个分支之间的角度) const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); // 当前点距圆心的位置 const distance = Math.random() * params.radius; const current = i * 3; positions[current] = Math.cos(branchAngle) * distance; positions[current + 1] = 0; positions[current + 2] = Math.sin(branchAngle) * distance; }将设置的count数更改为1000,则有以下效果
6-7 运用数学知识打造复杂形状臂旋星系03
-
目标1:为三个分支设置一定的轨迹
const params = { count: 1000, size: 0.1, radius: 5, branch: 3, color: "#ffffff", rotateScale: 0.3, // 设置点的旋转角度 }; // 循环生成顶点 for (let i = 0; i < params.count; i++) { // 当前的点应该在哪一条分支的角度上 const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); // 当前点距圆心的位置 const distance = Math.random() * params.radius; const current = i * 3; // 为每一个点的x, y, z分别加上一个[0-1]的随机数 const randomX = Math.random(); const randomY = Math.random(); const randomZ = Math.random(); // 让近原点的点旋转角度少一点,远原点的点旋转角度多一点,再加上一点偏移 positions[current] = Math.cos(branchAngle + distance * params.rotateScale) * distance + randomX; positions[current + 1] = 0 + randomY; positions[current + 2] = Math.sin(branchAngle + distance * params.rotateScale) * distance + randomZ; }这个时候的相机远面端要大于相机所在位置,否则看不全整个图像
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 20 // 值要大于相机所在位置的值 ); camera.position.set(0, 0, 10);
-
目标2:将各个分支上的点集中一下
// 先将点的数量增加至10000 for (let i = 0; i < params.count; i++) { // 当前的点应该在哪一条分支的角度上 const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); // 当前点距圆心的位置 const distance = Math.random() * params.radius; const current = i * 3; // 因为x, y, z值有正有负,因此用的是三次方 const randomX = Math.pow(Math.random() * 2 - 1, 3); // -1~1 const randomY = Math.pow(Math.random() * 2 - 1, 3); const randomZ = Math.pow(Math.random() * 2 - 1, 3); positions[current] = Math.cos(branchAngle + distance * params.rotateScale) * distance + randomX; positions[current + 1] = 0 + randomY; positions[current + 2] = Math.sin(branchAngle + distance * params.rotateScale) * distance + randomZ; }
-
目标3:将集中点后的分支上的点设置为——靠近原点位置多,远离原点位置少
for (let i = 0; i < params.count; i++) { const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); // 当前点距圆心的位置(将此处乘的半径在进行一次[0-1]的随机) const distance = Math.random() * params.radius * Math.pow(Math.random(), 3); const current = i * 3; const randomX = Math.pow(Math.random() * 2 - 1, 3); // -1~1 const randomY = Math.pow(Math.random() * 2 - 1, 3); const randomZ = Math.pow(Math.random() * 2 - 1, 3); positions[current] = Math.cos(branchAngle + distance * params.rotateScale) * distance + randomX; positions[current + 1] = 0 + randomY; positions[current + 2] = Math.sin(branchAngle + distance * params.rotateScale) * distance + randomZ; }
-
目标4:现在上下是平的,上下也做成靠近中间的点多,其它位置少
for (let i = 0; i < params.count; i++) { const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); // 当前点距圆心的位置 const distance = Math.random() * params.radius * Math.pow(Math.random(), 3); const current = i * 3; // 给每一个点加的随机数乘以(半径减去点距圆心的距离),再除以5——离得越远的点半径减去点距圆心的距离越短,即x, y, z偏移的越少,就形成了集中的效果 const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; positions[current] = Math.cos(branchAngle + distance * params.rotateScale) * distance + randomX; positions[current + 1] = 0 + randomY; positions[current + 2] = Math.sin(branchAngle + distance * params.rotateScale) * distance + randomZ; }
-
可以设置分支个数
// 设置参数 const params = { count: 10000, size: 0.1, radius: 5, branch: 6, // 设置分支为6个 color: "#ffffff", rotateScale: 0.3, };
6-8 运用颜色收敛方法设置星系臂旋渐变
-
目标:给星系上色,从中间向四周扩散,中间颜色亮,越往外颜色越暗
-
lerp(color: Color, alpha: Float): Color
color - 用于收敛的颜色
alpha - 介于0-1的数字
-
-
生成目标效果
// 设置颜色和结束颜色 const params = { count: 10000, size: 0.1, radius: 5, branch: 3, color: "#ff6030", // 颜色:红色 rotateScale: 0.3, endColor: "#1b3894", // 结束颜色:蓝色 }; // 创建颜色对象 const centerColor = new THREE.Color(params.color); const endColor = new THREE.Color(params.endColor); const generateGalaxy = () => { geometry = new THREE.BufferGeometry(); const positions = new Float32Array(params.count * 3); const colors = new Float32Array(params.count * 3); for (let i = 0; i < params.count; i++) { const branchAngle = (i % params.branch) * ((2 * Math.PI) / params.branch); const distance = Math.random() * params.radius * Math.pow(Math.random(), 3); const current = i * 3; const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5; positions[current] = Math.cos(branchAngle + distance * params.rotateScale) * distance + randomX; positions[current + 1] = 0 + randomY; positions[current + 2] = Math.sin(branchAngle + distance * params.rotateScale) * distance + randomZ; // 生成混合颜色,形成渐变色——先克隆颜色,再和结束颜色进行混合,最后设置点的rgb颜色值 const mixColor = centerColor.clone(); mixColor.lerp(endColor, distance / params.radius); colors[current] = mixColor.r; colors[current + 1] = mixColor.g; colors[current + 2] = mixColor.b; } geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3)); // 将生成的颜色设置给缓冲区 geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3)); material = new THREE.PointsMaterial({ // color: new THREE.Color(params.color), // 将材质颜色给注掉,否则会影响混合颜色的显示 size: params.size, sizeAttenuation: true, depthWrite: false, blending: THREE.AdditiveBlending, map: texture, alphaMap: texture, transparent: true, vertexColors: true, // 设置启用顶点颜色 }); points = new THREE.Points(geometry, material); scene.add(points); };
7-1 投射光线实现三维物体交互
-
光线投射(Raycaster)
-
先获取鼠标点在屏幕上的位置,x/y取值范围为-1~1
-
根据鼠标位置设置相机投射位置
-
setFromCamera(coords: Vector2, camera: Camera): null
coords——在标准化设备坐标中鼠标的二维坐标 - X分量与Y分量应当在-1~1之间
camera——射线所来源的摄像机
使用一个新的原点和方向来更新射线
-
-
相交物体检测(intersectObject 或 intersectObjects)
-
intersectObjects( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array
objects —— 检测和射线相交的一组物体。 recursive —— 若为true,则同时也会检测所有物体的后代。否则将只会检测对象本身的相交部分。默认值为true。 optionalTarget —— (可选)设置结果的目标数组。如果不设置这个值,则一个新的Array会被实例化;如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)
-
-
-
实现点击物体将物体的填充改成红色(点到哪个格子填充哪个格子)
// 创建立方体 const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); // 创建材质,设置网格显示,默认颜色为白色 const material = new THREE.MeshBasicMaterial({ wireframe: true, }); // 创建一个红色材质 const redMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // 创建1000个立方体 let cubeArr = []; for (let i = -5; i < 5; i++) { for (let j = -5; j < 5; j++) { for (let k = -5; k < 5; k++) { // 利用立方体和材质创建物体,存入数组 const cube = new THREE.Mesh(cubeGeometry, material); cube.position.set(i, j, k); scene.add(cube); cubeArr.push(cube); } } } // 创建投射光线对象 const raycaster = new THREE.Raycaster(); // 设置鼠标的位置对象 const mouse = new THREE.Vector2(); // 监听鼠标位置 window.addEventListener("click", (event) => { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; // X轴从左到右是-1~1 mouse.y = -((event.clientY / window.innerHeight) * 2 - 1); // Y轴从下到上是-1~1,和js屏幕坐标方向相反,因此是负的 // 设置投射位置位置 raycaster.setFromCamera(mouse, camera); let result = raycaster.intersectObjects(cubeArr); if (result[0]) { result[0].object.material = redMaterial; } });
9 应用物理引擎设置物体相互作用
9-1 认识物理引擎与cannon安装
之前使用的THREE创建物体以及操作是渲染引擎渲染的,真实世界的效果(比如两个球碰撞然后相互弹开)是由物理引擎渲染的,物理引擎和渲染引擎共同工作才能实现真实的效果。
cannon是一个物理引擎。
-
安装并导入
npm i cannon-es --save import * as CANNON from "cannon-es"; console.log(CANNON);
9-2 使用物理引擎关联Threejs物体
(1)创建小球和平面
// 创建球和平面同时设置阴影效果
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20);
const sphereMaterial = new THREE.MeshStandardMaterial();
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true; // 投射阴影
scene.add(sphere);
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshStandardMaterial()
);
floor.position.set(0, -5, 0);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true; // 接收阴影
scene.add(floor);
// 添加环境光和平行光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.castShadow = true; // 投射阴影
scene.add(directionalLight);
(2)创建物理环境的球体,并让它进行自由落体运动
// 创建物理世界
// 第一种方式
// const world = new CANNON.World({
// gravity: new CANNON.Vec3(0, -9.8, 0), // 为物理环境设置重力
// });
// 第二种方式
const world = new CANNON.World();
// 为物理环境设置重力
world.gravity.set(0, -9.8, 0);
// 创建物理世界的小球
const sphereShape = new CANNON.Sphere(1);
// 设置物体材质
const sphereWorldMaterial = new CANNON.Material("iron");
// 创建小球物体
const sphereBody = new CANNON.Body({
mass: 1, // 质量
position: new CANNON.Vec3(0, 0, 0),
shape: sphereShape,
// 物体的材质
material: sphereWorldMaterial,
});
// 将物体添加至物理世界
world.addBody(sphereBody);
// 这个时候只在物理世界创建了小球,它还不能运动,因为没有跟renderer渲染器渲染的小球进行关联
function render() {
let deltaTime = clock.getDelta();
// 更新物理引擎里世界的物体,每秒120帧
world.step(1 / 120, deltaTime);
// 设置renderer渲染器中的小球位置和物体世界中的小球同步
sphere.position.copy(sphereBody.position);
controls.update();
renderer.render(scene, camera);
// 渲染下一帧的时候调用render函数
requestAnimationFrame(render);
}
// 这下就能看见小球在做自由落体运动
9-3 设置固定不动的地面与小球碰撞
-
创建物理世界的地板
const floorShape = new CANNON.Plane(); const floorWorldMaterial = new CANNON.Material("floor"); const floorBody = new CANNON.Body(); floorBody.mass = 0; // 固定不动 floorBody.addShape(floorShape); // 设置地面位置 floorBody.position.set(0, -5, 0); // 旋转地面的位置(按照第一个参数的向量方向,旋转第二个参数的角度) floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2); floorBody.material = floorWorldMaterial; world.addBody(floorBody);
9-4 监听碰撞事件和控制碰撞音效
-
准备素材,可自行下载
-
创建碰撞事件,使其碰撞时有音效
// 创建击打声音 const hitSound = new Audio("assets/metalHit.mp3"); // 添加监听碰撞事件 function HitEvent(e) { console.log(e); // 获取碰撞强度 const impactStrength = e.contact.getImpactVelocityAlongNormal(); console.log(impactStrength); // 强度大于5的时候,播放音效,播放音效的前提是需要交互,谷歌浏览器不支持自动出现音效 if (impactStrength > 5) { hitSound.play(); } } sphereBody.addEventListener("collide", HitEvent);
9-5 关联材质设置摩擦与弹性系数
(1)设置物理世界物体的材料名称
// 设置球体物体材质
const sphereWorldMaterial = new CANNON.Material("sphere");
// 设置平面物体材质
const floorWorldMaterial = new CANNON.Material("floor");
(2)设置两种材质碰撞的参数
// 设置两种材质碰撞的参数
const defaultContactMaterial = new CANNON.ContactMaterial(
sphereWorldMaterial, // 第一种材质
floorWorldMaterial, // 第二种材质
{
friction: 0.1, // 摩擦系数
restitution: 0.7, // 恢复系数(弹性)
}
);
// 将材料的关联设置添加到物理世界中
world.defaultContactMaterial = defaultContactMaterial;
(3)监听碰撞事件中重新设置音效参数
// 添加监听碰撞事件
function HitEvent(e) {
console.log(e);
// 获取碰撞强度
const impactStrength = e.contact.getImpactVelocityAlongNormal();
console.log(impactStrength);
if (impactStrength > 1) {
// 每一次碰撞都重新播放音效
hitSound.currentTime = 0;
// 设置音效的音量,范围[0-1]
hitSound.volume =
hitSound.volume - 0.2 < 0 ? hitSound.volume : hitSound.volume - 0.2;
// 播放音效
hitSound.play();
}
}
sphereBody.addEventListener("collide", HitEvent);
9-6 立方体相互碰撞后旋转效果
(1)平面和其余不变,将立方体的操作封装为一个函数
// 设置多个立方体,使用数组
const cubeArr = [];
// 设置物体材质(写在外面,默认材料使用)
const cubeWorldMaterial = new CANNON.Material("cube");
function createCube() {
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial();
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
scene.add(cube);
// 创建物理世界的立方体
const cubeShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5)); // 这里要写宽度的一般
const cubeBody = new CANNON.Body({
mass: 1, // 质量
position: new CANNON.Vec3(0, 0, 0),
shape: cubeShape,
// 物体的材质
material: cubeWorldMaterial,
});
// 将物体添加至物理世界
world.addBody(cubeBody);
// 添加监听碰撞事件
function HitEvent(e) {
console.log(e);
// 获取碰撞强度
const impactStrength = e.contact.getImpactVelocityAlongNormal();
console.log(impactStrength);
if (impactStrength > 1) {
hitSound.currentTime = 0;
hitSound.volume = impactStrength / 12 > 1 ? 1 : impactStrength / 12;
console.log(hitSound.volume);
hitSound.play();
}
}
cubeBody.addEventListener("collide", HitEvent);
cubeArr.push({
mesh: cube,
body: cubeBody,
});
}
(2)渲染函数中将渲染器物体和物理物体相关联
function render() {
let deltaTime = clock.getDelta();
// 更新物理引擎里世界的物体
world.step(1 / 120, deltaTime);
cubeArr.forEach((item) => {
item.mesh.position.copy(item.body.position);
// 设置渲染的物体跟随物理的旋转
item.mesh.quaternion.copy(item.body.quaternion);
});
controls.update();
renderer.render(scene, camera);
// 渲染下一帧的时候调用render函数
requestAnimationFrame(render);
}
(3)设置点击创建立方体
window.addEventListener("click", () => {
createCube();
});
9-7 给物体施加作用力
cubeBody.applyLocalForce(
// 添加的力的大小和方向
new CANNON.Vec3(180, 0, 0),
// 施加的力所在的位置
new CANNON.Vec3(0, 0, 0)
);
能观察到立方体是偏移的