这个案例主要是使用three.js中的物理引擎插件(OimoPhysics),使用OimoPhysics可以使three.js中的物体具有更真实的物理效果,例如重力、自由落体、弹簧等。具体效果如下:
首先需要搭建基础环境,场景、相机、渲染器和控制器。若不清楚的可以查看three.js基础案例day01。
1.创建多个立方体
boxes = new THREE.InstancedMesh(
new THREE.BoxGeometry(0.1, 0.1, 0.1),
new THREE.MeshLambertMaterial(),
200,
)
const matrix = new THREE.Matrix4()
const color = new THREE.Color()
for (let i = 0; i < boxes.count; i++) {
matrix.setPosition(
Math.random() - 0.5,
Math.random() * 2,
Math.random() - 0.5,
)
boxes.setMatrixAt(i, matrix)
boxes.setColorAt(i, color.setHex(Math.random() * 0xffffff))
}
scene.add(boxes)
2.创建多个小球
spheres = new THREE.InstancedMesh(
new THREE.SphereGeometry(0.075, 32, 16),
new THREE.MeshLambertMaterial(),
200,
)
for (let i = 0; i < spheres.count; i++) {
matrix.setPosition(
Math.random() - 0.5,
Math.random() * 2,
Math.random() - 0.5,
)
spheres.setMatrixAt(i, matrix)
spheres.setColorAt(i, color.setHex(Math.random() * 0xffffff))
}
scene.add(spheres)
3.创建平面 并设置阴影
// 平面
plane = new THREE.Mesh(
new THREE.BoxGeometry(10, 1, 10),
new THREE.ShadowMaterial({ color: 0x111111 }), // 影子的颜色
)
plane.position.y = -1
scene.add(plane)
// 设置阴影
function enableShadow() {
renderer.shadowMap.enabled = true
dirLight.castShadow = true
plane.receiveShadow = true
boxes.castShadow = true
boxes.receiveShadow = true
}
4. 导入OimoPhysics插件并使用
// 导入
import { OimoPhysics } from 'three/examples/jsm/physics/OimoPhysics'
// 使用
physics = await OimoPhysics()
physics.addMesh(plane)
physics.addMesh(boxes, 1)
physics.addMesh(spheres, 1)
5.设置物体不断的落下
let index = Math.floor(Math.random() * boxes.count)
position.set(0, Math.random() + 1, 0)
physics.setMeshPosition(boxes, position, index)
index = Math.floor(Math.random() * spheres.count)
position.set(0, Math.random() + 1, 0)
physics.setMeshPosition(spheres, position, index)
controls.update()
全部代码
<template>
<div id="threeId" ref="elementRef"></div>
</template>
<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { OimoPhysics } from 'three/examples/jsm/physics/OimoPhysics'
import { ref, onMounted } from 'vue'
let width, height, scene, camera, renderer, controls
let dirLight
let boxes, plane, spheres
let physics
let position = new THREE.Vector3()
const elementRef = ref(null)
onMounted(() => {
const element = elementRef.value
width = element.offsetWidth
height = element.offsetHeight
initScene()
initLights()
initMeshes()
enableShadow()
enablePhysics()
})
function initScene() {
// 初始化场景: 创建场景,相机,物体,渲染器
scene = new THREE.Scene()
scene.background = new THREE.Color(0x808080)
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000)
camera.position.set(2, 1, 2)
scene.add(camera)
// const axesHelper = new THREE.AxesHelper(40)
// scene.add(axesHelper)
renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height)
elementRef.value.appendChild(renderer.domElement)
renderer.outputEncoding = THREE.sRGBEncoding
controls = new OrbitControls(camera, renderer.domElement)
controls.update()
}
function initLights() {
// 环境光
const hesLight = new THREE.HemisphereLight() // 白色光
hesLight.intensity = 0.3
scene.add(hesLight)
// 平行光
dirLight = new THREE.DirectionalLight()
dirLight.position.set(5, 5, -5)
scene.add(dirLight)
}
function initMeshes() {
// plane
plane = new THREE.Mesh(
new THREE.BoxGeometry(10, 1, 10),
new THREE.ShadowMaterial({ color: 0x111111 }), // 影子的颜色
)
plane.position.y = -1
scene.add(plane)
// 立方体 材质:如木头,镜面反色差
boxes = new THREE.InstancedMesh(
new THREE.BoxGeometry(0.1, 0.1, 0.1),
new THREE.MeshLambertMaterial(),
200,
)
const matrix = new THREE.Matrix4()
const color = new THREE.Color()
for (let i = 0; i < boxes.count; i++) {
matrix.setPosition(
Math.random() - 0.5,
Math.random() * 2,
Math.random() - 0.5,
)
boxes.setMatrixAt(i, matrix)
boxes.setColorAt(i, color.setHex(Math.random() * 0xffffff))
}
scene.add(boxes)
// 球体
spheres = new THREE.InstancedMesh(
new THREE.SphereGeometry(0.075, 32, 16),
new THREE.MeshLambertMaterial(),
200,
)
for (let i = 0; i < spheres.count; i++) {
matrix.setPosition(
Math.random() - 0.5,
Math.random() * 2,
Math.random() - 0.5,
)
spheres.setMatrixAt(i, matrix)
spheres.setColorAt(i, color.setHex(Math.random() * 0xffffff))
}
scene.add(spheres)
}
function enableShadow() {
renderer.shadowMap.enabled = true
dirLight.castShadow = true
plane.receiveShadow = true
boxes.castShadow = true
boxes.receiveShadow = true
}
async function enablePhysics() {
physics = await OimoPhysics()
physics.addMesh(plane)
physics.addMesh(boxes, 1)
physics.addMesh(spheres, 1)
render()
}
function render() {
requestAnimationFrame(render)
let index = Math.floor(Math.random() * boxes.count)
position.set(0, Math.random() + 1, 0)
physics.setMeshPosition(boxes, position, index)
index = Math.floor(Math.random() * spheres.count)
position.set(0, Math.random() + 1, 0)
physics.setMeshPosition(spheres, position, index)
controls.update()
renderer.render(scene, camera)
}
</script>
<style lang="scss" scoped>
#threeId {
width: 100%;
height: 100%;
}
</style>