一、演示效果

cannon引擎物体碰撞

二、three.js基本步骤

导入,创建场景、相机、渲染器等

三、难点实现

(1)创建一个平面

// 创建平面
const floor = new THREE.Mesh(
    new THREE.PlaneGeometry(20, 20),
    new THREE.MeshStandardMaterial()
)
floor.receiveShadow = true
floor.position.set(0, -5, 0)
floor.rotation.x = -Math.PI / 2
scene.add(floor)

(2)创建物理世界的平面,并且点击创建立方体


//创建物理世界
const world = new CANNON.World()
world.gravity.set(0, -9.8, 0) //设置物理重力9.8牛
const cubeArr = []
const cancleMaterial = new CANNON.Material('cube')
function createCubeAndBody() {
    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({
        shape: cubeShape, //形状
        position: new CANNON.Vec3(0, 0, 0),
        mass: 1, //质量
        material: cancleMaterial,
    })
    //给物体施加一个 力
    cubeBody.applyLocalForce(
        new CANNON.Vec3(300, 0, 0), //施加力的大小和方向
        new CANNON.Vec3(0, 0, 0) //施加力的位置
    )
    world.addBody(cubeBody)
    const audio = new Audio('/static/click_sound.wav')

    //添加监听碰撞事件
    function hitEvent(e) {
        //碰撞强度
        const impactStrength = e.contact.getImpactVelocityAlongNormal()
        if (impactStrength > 2) {
            audio.currentTime = 0
            audio.volume = impactStrength / 12
            audio.play()
        }
    }
    cubeBody.addEventListener('collide', hitEvent)

    cubeArr.push({
        mesh: cube,
        body: cubeBody,
    })
}

window.addEventListener('click', createCubeAndBody)

四、完整代码

import * as THREE from 'three'

//目标:学习使用cannon引擎

//导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

//导入cannon引擎
import * as CANNON from 'cannon-es'

//1、创建场景
const scene = new THREE.Scene()

//2、创建相机
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
) // 参数分别代表,相机角度、屏幕宽高比、近端点,远端点

//设置相机位置
camera.position.set(0, 0, 10)
scene.add(camera)

//创建立方体和平面

const floor = new THREE.Mesh(
    new THREE.PlaneGeometry(20, 20),
    new THREE.MeshStandardMaterial()
)
floor.receiveShadow = true
floor.position.set(0, -5, 0)
floor.rotation.x = -Math.PI / 2
scene.add(floor)

//添加环境光和平行光
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
const dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.castShadow = true
scene.add(ambientLight)
scene.add(dirLight)

//创建物理世界
const world = new CANNON.World()
world.gravity.set(0, -9.8, 0) //设置物理重力9.8牛
const cubeArr = []
const cancleMaterial = new CANNON.Material('cube')
function createCubeAndBody() {
    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({
        shape: cubeShape, //形状
        position: new CANNON.Vec3(0, 0, 0),
        mass: 1, //质量
        material: cancleMaterial,
    })
    //给物体施加一个 力
    cubeBody.applyLocalForce(
        new CANNON.Vec3(300, 0, 0), //施加力的大小和方向
        new CANNON.Vec3(0, 0, 0) //施加力的位置
    )
    world.addBody(cubeBody)
    const audio = new Audio('/static/click_sound.wav')

    //添加监听碰撞事件
    function hitEvent(e) {
        //碰撞强度
        const impactStrength = e.contact.getImpactVelocityAlongNormal()
        if (impactStrength > 2) {
            audio.currentTime = 0
            audio.volume = impactStrength / 12
            audio.play()
        }
    }
    cubeBody.addEventListener('collide', hitEvent)

    cubeArr.push({
        mesh: cube,
        body: cubeBody,
    })
}

window.addEventListener('click', createCubeAndBody)

//创建物理世界地面
const floorShape = new CANNON.Plane()
const floorMaterial = new CANNON.Material('floor')
const floorBody = new CANNON.Body({
    mass: 0, //设置为0表示无论怎么碰撞,位置都不会变
    shape: floorShape,
    position: new CANNON.Vec3(0, -5, 0),
    material: floorMaterial,
})
floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
world.addBody(floorBody)

const sphereConcatFloorMaterial = new CANNON.ContactMaterial(
    cancleMaterial,
    floorMaterial,
    {
        restitution: 0.7, //弹性
        friction: 0.1, //摩擦系数摩擦力
    }
)

//将关联材质添加到物理世界中
world.addContactMaterial(sphereConcatFloorMaterial)

//设置默认关联材质,如果不设置,默认使用这个关联材质
world.defaultContactMaterial = sphereConcatFloorMaterial

//初始化渲染器
const renderer = new THREE.WebGLRenderer()

//设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true
renderer.physicallyCorrectLights = true

//将webGL渲染的canvas添加到app中
document.getElementById('app').appendChild(renderer.domElement)

//创建控制器
const controls = new OrbitControls(camera, renderer.domElement)

//设置控制器阻尼,让滑动更有真实感
controls.enableDamping = true

//创建坐标轴
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)

//设置时钟
const clock = new THREE.Clock()

render()

//渲染函数
function render() {
    const time = clock.getElapsedTime()
    let dateTime = clock.getDelta()
    //更新物理引擎
    // world.step(1 / 120, dateTime)
    world.step(1 / 120)
    cubeArr.forEach((item) => {
        item.mesh.position.copy(item.body.position)
        //设置渲染世界的物体跟随真实世界物体旋转
        item.mesh.quaternion.copy(item.body.quaternion)
    })
    controls.update()
    renderer.render(scene, camera)
    //下一帧渲染完毕再次执行,保证每一帧都渲染
    requestAnimationFrame(render)
}

Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐