最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Threejs實(shí)現(xiàn)物理運(yùn)動(dòng)模擬

 更新時(shí)間:2026年03月23日 08:39:53   作者:誰(shuí)在黃金彼岸  
本文主要介紹了Threejs實(shí)現(xiàn)物理運(yùn)動(dòng)模擬,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、物理運(yùn)動(dòng)原理

在 Three.js 中實(shí)現(xiàn)物理運(yùn)動(dòng)的核心思想是:

  1. Three.js 負(fù)責(zé)渲染:Three.js 是一個(gè)圖形庫(kù),用于創(chuàng)建和渲染 3D 場(chǎng)景。
  2. 物理引擎負(fù)責(zé)計(jì)算:物理引擎(如 Cannon.js或 Ammo.js, 本文用的是前一種)模擬現(xiàn)實(shí)世界的物理規(guī)則,例如重力、碰撞、反彈等。
  3. 數(shù)據(jù)同步:在每一幀中,將物理引擎計(jì)算的結(jié)果(如物體的位置、速度、旋轉(zhuǎn)等)同步到 Three.js 的對(duì)象上。

二、實(shí)現(xiàn)步驟詳解

1. 初始化 Three.js 場(chǎng)景

在 initScene 方法中,創(chuàng)建 Three.js 的基本組件(場(chǎng)景、相機(jī)、渲染器等)。

function initScene() {
  if (!canvas.value) return
  makeRenderer(canvas.value.clientWidth, canvas.value.clientHeight)
  makeScene()
  makeCamera(canvas.value.clientWidth, canvas.value.clientHeight)
  orbitControls()
  addSky()
  addGround()
}
  • 初始化 WebGL 渲染器,并設(shè)置畫(huà)布大小和設(shè)備像素比。 makeRenderer
  • 創(chuàng)建 Three.js 場(chǎng)景,并添加光源。 makeScene
  • 創(chuàng)建透視相機(jī),并設(shè)置初始位置。 makeCamera
  • 添加軌道控制器,用于控制相機(jī)視角。 orbitControls
  • 和 :分別添加天空和地面。 addSky``addGround

2. 創(chuàng)建物理世界

使用 Cannon.js 創(chuàng)建物理世界,并設(shè)置重力。

world = new Cannon.World()
world.gravity.set(0, -9.82, 0) // 設(shè)置重力 (m/s2)
  • world.gravity.set(0, -9.82, 0):設(shè)置重力方向?yàn)橄蛳拢╕ 軸負(fù)方向),模擬地球重力。

3. 創(chuàng)建物體并同步到物理引擎

在 Three.js 和物理引擎中分別創(chuàng)建物體,并保持它們之間的同步。

創(chuàng)建球體(Three.js 對(duì)象)

sphere = new THREE.Mesh(
  new THREE.SphereGeometry(2, 32, 32),
  new THREE.MeshBasicMaterial({ color: 0xff11ff }),
)
sphere.position.set(0, 10, 0)
scene.add(sphere)
  • 使用 THREE.SphereGeometry 創(chuàng)建一個(gè)半徑為 2 的球體。
  • 將球體添加到 Three.js 場(chǎng)景中。

創(chuàng)建對(duì)應(yīng)的物理剛體(Cannon.js 對(duì)象)

const sphereBody = new Cannon.Body({
  mass: 1,
  shape: new Cannon.Sphere(2),
  position: new Cannon.Vec3(sphere.position.x, sphere.position.y, sphere.position.z),
})
world.addBody(sphereBody)
  • 使用 Cannon.Sphere 創(chuàng)建一個(gè)物理剛體,并設(shè)置質(zhì)量為 1。
  • 將物理剛體添加到物理世界中。

添加地面

const groundBody = new Cannon.Body({
  mass: 0,
  shape: new Cannon.Plane(),
})
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
world.addBody(groundBody)
  • 創(chuàng)建一個(gè)靜態(tài)的平面作為地面(mass: 0 表示靜態(tài)物體)。
  • 使用 setFromEuler 旋轉(zhuǎn)平面使其水平。

4. 動(dòng)畫(huà)循環(huán)

在每一幀中更新物理引擎的狀態(tài),并同步到 Three.js 場(chǎng)景。

function animate() {
  animationFrameId = requestAnimationFrame(animate)

  // 更新物理引擎
  updatePhysic()

  // 渲染場(chǎng)景
  renderer.render(scene, camera)
}

function updatePhysic() {
  world.step(1 / 60) // 模擬 60 幀每秒
  sphere.position.copy(sphereBody.position) // 同步位置
}
  • world.step(1 / 60):更新物理引擎的世界狀態(tài),模擬 60 幀每秒。
  • sphere.position.copy(sphereBody.position):將物理引擎計(jì)算的結(jié)果同步到 Three.js 的球體對(duì)象上。

5. 處理窗口大小調(diào)整

當(dāng)窗口大小發(fā)生變化時(shí),重新調(diào)整渲染器和相機(jī)的參數(shù)。

function onWindowResize() {
  if (!canvas.value) return
  const width = canvas.value.clientWidth
  const height = canvas.value.clientHeight

  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}
  • 調(diào)整相機(jī)的寬高比,并更新投影矩陣。
  • 重新設(shè)置渲染器的尺寸。

三、完整流程總結(jié)

  1. 初始化 Three.js 場(chǎng)景
    • 創(chuàng)建場(chǎng)景、相機(jī)、渲染器。
    • 添加光源、天空、地面等元素。
  2. 初始化物理引擎
    • 創(chuàng)建物理世界,并設(shè)置重力。
    • 添加地面和物體的物理剛體。
  3. 動(dòng)畫(huà)循環(huán)
    • 在每一幀中更新物理引擎的狀態(tài)。
    • 將物理引擎的結(jié)果同步到 Three.js 對(duì)象上。
    • 渲染場(chǎng)景。
  4. 處理窗口大小調(diào)整
    • 監(jiān)聽(tīng)窗口大小變化事件,動(dòng)態(tài)調(diào)整相機(jī)和渲染器的參數(shù)。

四、關(guān)鍵點(diǎn)解析

1. 數(shù)據(jù)同步

  • 物理引擎中的物體(如 sphereBody)和 Three.js 中的物體(如 sphere)需要保持同步。
  • 在每一幀中,通過(guò) sphere.position.copy(sphereBody.position) 實(shí)現(xiàn)位置同步。

2. 性能優(yōu)化

  • 使用 requestAnimationFrame 創(chuàng)建動(dòng)畫(huà)循環(huán),確保流暢的動(dòng)畫(huà)效果。
  • 設(shè)置物理引擎的更新頻率(如 world.step(1 / 60)),避免過(guò)度計(jì)算。

3. 物理材質(zhì)

  • 在代碼中,定義了兩種物理材質(zhì)(groundMaterial 和 sphereMaterial),并通過(guò) 設(shè)置摩擦系數(shù)和彈性系數(shù)。 ContactMaterial
  • 這些參數(shù)影響物體的碰撞行為,例如反彈高度和滑動(dòng)距離。

五、源碼

<template>
  <el-container style="width: 100%; height: 100%">
    <!-- 頭部標(biāo)題 -->
    <el-header class="header"> 物理運(yùn)動(dòng)</el-header>
    <!-- 主要內(nèi)容區(qū)域,包含 Three.js 畫(huà)布 -->
    <el-main class="canvas-container">
      <canvas ref="canvas" class="canvas"></canvas>
    </el-main>
  </el-container>
</template>
<script lang="ts">
export default {
  name: 'PhysicalMotion', // 使用多詞名稱(chēng)
}
</script>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { Sky } from 'three/examples/jsm/objects/Sky'
import { ShaderMaterial, MathUtils } from 'three'
import * as Cannon from 'cannon'
// Three.js 相關(guān)變量聲明
// 場(chǎng)景
let scene: THREE.Scene
// 透視相機(jī)
let camera: THREE.PerspectiveCamera
// WebGL渲染器
let renderer: THREE.WebGLRenderer
// 天空
let sky: THREE.Mesh
// 動(dòng)畫(huà)幀ID
let animationFrameId: number
let controls: InstanceType<typeof OrbitControls>
let world: InstanceType<typeof Cannon.World>
let sphere: THREE.Mesh
let sphereBody: InstanceType<typeof Cannon.Body>
// 畫(huà)布引用
const canvas = ref<HTMLCanvasElement>()
// 組件掛載時(shí)初始化場(chǎng)景并開(kāi)始動(dòng)畫(huà)
onMounted(() => {
  initScene()
  animate()
  // 添加窗口大小改變事件監(jiān)聽(tīng)
  window.addEventListener('resize', onWindowResize)
})
// 初始化場(chǎng)景
function initScene() {
  if (!canvas.value) return
  makeRenderer(canvas.value.clientWidth, canvas.value.clientHeight)
  makeScene()
  // 創(chuàng)建圓形
  sphere = new THREE.Mesh(
    new THREE.SphereGeometry(2, 32, 32),
    new THREE.MeshBasicMaterial({ color: 0xff11ff }),
  )
  sphere.position.set(0, 10, 0)
  scene.add(sphere)
  world = new Cannon.World()
  world.gravity.set(0, -9.82, 0)
  //創(chuàng)建物理材料
  const groundMaterial = new Cannon.Material("groundMaterial")
  const sphereMaterial = new Cannon.Material("sphereMaterial")
  const contactMaterial = new Cannon.ContactMaterial(groundMaterial, sphereMaterial, {
    friction: 0.3,
    restitution: 0.5,
  })
  world.addContactMaterial(contactMaterial)
  sphereBody = new Cannon.Body({
    mass: 1,
    shape: new Cannon.Sphere(2),
    position: new Cannon.Vec3(sphere.position.x, sphere.position.y, sphere.position.z),
    material: sphereMaterial,
  })
  world.addBody(sphereBody)
  const groundBody = new Cannon.Body({
    mass: 0,
    shape: new Cannon.Plane(),
    material: groundMaterial,
  })
  groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
  world.addBody(groundBody)
  makeCamera(canvas.value.clientWidth, canvas.value.clientHeight)
  orbitControls()
  addSky()
  addGround()
}
function updatePhysic(){
  world.step(1 / 60)
  sphere.position.copy(sphereBody.position)
}
function makeScene() {
  // 創(chuàng)建場(chǎng)景
  scene = new THREE.Scene()
  //添加光源
  const light = new THREE.DirectionalLight(0xffffff, 1)
  scene.add(light)
}
function makeRenderer(width: number, height: number) {
  // 創(chuàng)建WebGL渲染器
  renderer = new THREE.WebGLRenderer({ canvas: canvas.value, antialias: true })
  renderer.setSize(width, height)
  // 設(shè)置設(shè)備像素比,確保在高分辨率屏幕上清晰顯示
  renderer.setPixelRatio(window.devicePixelRatio)
}
function makeCamera(width: number, height: number) {
  // 創(chuàng)建透視相機(jī)
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
  // 設(shè)置相機(jī)位置
  camera.position.set(0, 10, 20)
  camera.lookAt(0, 0, 0)
}
function orbitControls() {
  controls = new OrbitControls(camera, canvas.value)
  // 限制攝像機(jī)的極角范圍(單位是弧度)
  controls.minPolarAngle = 0 // 最小角度,0 表示水平視角
  controls.maxPolarAngle = MathUtils.degToRad(85) // 最大角度,例如 85 度
  // 可選:限制攝像機(jī)的縮放范圍
  controls.minDistance = 5 // 最小距離
  controls.maxDistance = 100 // 最大距離
}
function addSky() {
  sky = new Sky()
  sky.scale.setScalar(450000) // 設(shè)置天空的縮放比例
  scene.add(sky)
  const sun = new THREE.Vector3()
  // 配置天空參數(shù)
  const effectController = {
    turbidity: 10,
    rayleigh: 2,
    mieCoefficient: 0.005,
    mieDirectionalG: 0.8,
    elevation: 2,
    azimuth: 180,
    exposure: renderer.toneMappingExposure,
  }
  const uniforms = (sky.material as ShaderMaterial).uniforms
  uniforms['turbidity'].value = effectController.turbidity
  uniforms['rayleigh'].value = effectController.rayleigh
  uniforms['mieCoefficient'].value = effectController.mieCoefficient
  uniforms['mieDirectionalG'].value = effectController.mieDirectionalG
  const phi = THREE.MathUtils.degToRad(90 - effectController.elevation)
  const theta = THREE.MathUtils.degToRad(effectController.azimuth)
  sun.setFromSphericalCoords(1, phi, theta)
  uniforms['sunPosition'].value.copy(sun)
}
// 添加大地
function addGround() {
  const groundGeometry = new THREE.PlaneGeometry(100, 100)
  const groundMaterial = new THREE.MeshStandardMaterial({
    color: 0x7ec850, // 綠色草地顏色
    side: THREE.DoubleSide,
  })
  const ground = new THREE.Mesh(groundGeometry, groundMaterial)
  ground.rotation.x = Math.PI / 2 // 旋轉(zhuǎn)平面使其水平
  scene.add(ground)
}
// 動(dòng)畫(huà)循環(huán)函數(shù)
function animate() {
  animationFrameId = requestAnimationFrame(animate)
  controls.update()
  updatePhysic()
  // 渲染場(chǎng)景
  renderer.render(scene, camera)
}
// 清理函數(shù)
function cleanup() {
  // 取消動(dòng)畫(huà)幀`
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId)
  }
  // 清理渲染器
  if (renderer) {
    // 移除所有事件監(jiān)聽(tīng)器
    renderer.domElement.removeEventListener('resize', onWindowResize)
    // 釋放渲染器資源
    renderer.dispose()
    // 清空渲染器
    renderer.forceContextLoss()
    // 移除畫(huà)布
    renderer.domElement.remove()
  }
  // 清理場(chǎng)景
  if (scene) {
    // 遍歷場(chǎng)景中的所有對(duì)象
    scene.traverse((object) => {
      if (object instanceof THREE.Mesh) {
        // 釋放幾何體資源
        object.geometry.dispose()
        // 釋放材質(zhì)資源
        if (Array.isArray(object.material)) {
          object.material.forEach((material) => material.dispose())
        } else {
          object.material.dispose()
        }
      }
    })
    // 清空?qǐng)鼍?
    scene.clear()
  }
  // 清理相機(jī)
  if (camera) {
    camera.clear()
  }
}
// 窗口大小改變時(shí)的處理函數(shù)
function onWindowResize() {
  if (!canvas.value) return
  const width = canvas.value.clientWidth
  const height = canvas.value.clientHeight
  console.log('onWindowResize', width, height)
  // 更新相機(jī)
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  // 更新渲染器
  renderer.setSize(width, height)
  // 確保渲染器的像素比與設(shè)備匹配
  renderer.setPixelRatio(window.devicePixelRatio)
}
// 組件卸載時(shí)清理資源
onUnmounted(() => {
  cleanup()
  // 移除窗口大小改變事件監(jiān)聽(tīng)
  window.removeEventListener('resize', onWindowResize)
})
</script>
<style scoped>
/* 頭部樣式 */
.header {
  text-align: center;
  font-size: 20px;
  font-weight: bold;
  color: #333;
}
/* 畫(huà)布容器樣式 */
.canvas-container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
/* 畫(huà)布樣式   // 保持畫(huà)布比例*/
.canvas {
  width: 100%;
  height: 100%;
}
</style>

到此這篇關(guān)于Threejs實(shí)現(xiàn)物理運(yùn)動(dòng)模擬的文章就介紹到這了,更多相關(guān)Threejs 物理運(yùn)動(dòng)模擬內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

丰宁| 柳州市| 绥阳县| 郎溪县| 清涧县| 育儿| 姜堰市| 南江县| 中方县| 拉孜县| 新疆| 吴忠市| 安新县| 金塔县| 迁西县| 东乡| 双江| 剑阁县| 福建省| 九台市| 和政县| 凤翔县| 永昌县| 昌乐县| 佛冈县| 桦川县| 桐城市| 萨迦县| 固阳县| 马龙县| 湛江市| 南皮县| 耒阳市| 黄龙县| 芜湖市| 鸡泽县| 偃师市| 越西县| 阿克| 苏尼特左旗| 郓城县|