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

Three.js頂點(diǎn)射線碰撞檢測(cè)具體實(shí)現(xiàn)步驟詳解

 更新時(shí)間:2026年03月09日 09:34:01   作者:晴晴723734  
在three.js中射線檢測(cè)是一種常用的技術(shù),用于模擬光線在3D空間中的傳播并找出其與場(chǎng)景中其他物體的碰撞,這篇文章主要介紹了Three.js頂點(diǎn)射線碰撞檢測(cè)具體實(shí)現(xiàn)步驟的相關(guān)資料,需要的朋友可以參考下

一、基本思路

核心算法流程:

第1步:遍歷幾何體所有頂點(diǎn),分別創(chuàng)建與幾何體中心坐標(biāo)構(gòu)成的射線

對(duì)于 每個(gè)幾何體A 的 每個(gè)頂點(diǎn)V:
   頂點(diǎn)位置 = V 的世界坐標(biāo)位置
   中心位置 = 幾何體A 的世界坐標(biāo)中心點(diǎn)
   方向向量 = 中心位置 - 頂點(diǎn)位置
   
   創(chuàng)建射線Raycaster:
      起點(diǎn) = 頂點(diǎn)位置
      方向 = 方向向量(歸一化)

實(shí)現(xiàn)細(xì)節(jié):

  • 獲取幾何體的頂點(diǎn)坐標(biāo)數(shù)組(Float32Array,每3個(gè)值表示一個(gè)頂點(diǎn)的x,y,z坐標(biāo))
  • 將頂點(diǎn)從局部坐標(biāo)系轉(zhuǎn)換到世界坐標(biāo)系
  • 計(jì)算幾何體的世界中心點(diǎn)(通常是物體位置)
  • 對(duì)每個(gè)頂點(diǎn)創(chuàng)建從頂點(diǎn)指向中心的射線

第2步:射線交叉計(jì)算

對(duì)于 幾何體A 的 每條射線R:
   使用Raycaster的intersectObject()方法:
       檢測(cè)射線R 是否與 幾何體B 相交
       如果相交:
           獲取交點(diǎn)信息(交點(diǎn)坐標(biāo)、距離、面信息等)

關(guān)鍵點(diǎn):

  • Raycaster.intersectObject(幾何體B) 返回交點(diǎn)數(shù)組
  • 交點(diǎn)數(shù)組按距離從近到遠(yuǎn)排序
  • 如果射線與幾何體B有交點(diǎn),說(shuō)明這條射線可能穿過(guò)了幾何體B

第3步:通過(guò)距離判斷兩個(gè)網(wǎng)格模型是否碰撞

對(duì)于 幾何體A 的 每條射線R 的 每個(gè)交點(diǎn)I:
   計(jì)算:起點(diǎn)到交點(diǎn)的距離 = 距離1
   計(jì)算:起點(diǎn)到幾何體A中心的距離 = 距離2
   
   如果 距離1 < 距離2:
       說(shuō)明射線在到達(dá)自己中心之前就擊中了對(duì)方
       判斷為"這條射線穿過(guò)了對(duì)方幾何體"
   
   如果 至少有一條射線穿過(guò)對(duì)方幾何體:
       判斷為"兩個(gè)幾何體發(fā)生碰撞"

邏輯解釋:

  • 射線是從自己頂點(diǎn)指向自己中心的
  • 如果射線在到達(dá)自己中心之前就擊中了對(duì)方,說(shuō)明對(duì)方在自己"內(nèi)部"或"前方"
  • 如果射線先到達(dá)自己中心,說(shuō)明對(duì)方在自己"后方"或"外部"

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

Step 1: 獲取幾何體所有頂點(diǎn)(世界坐標(biāo))

// 1.1 獲取幾何體的頂點(diǎn)位置數(shù)據(jù)
const geometry = mesh.geometry
const positionAttribute = geometry.attributes.position
const positions = positionAttribute.array  // [x1, y1, z1, x2, y2, z2, ...]

// 1.2 獲取世界變換矩陣
const matrixWorld = mesh.matrixWorld

// 1.3 遍歷所有頂點(diǎn),轉(zhuǎn)換為世界坐標(biāo)
const worldVertices = []
for (let i = 0; i < positions.length; i += 3) {
  // 創(chuàng)建局部坐標(biāo)頂點(diǎn)
  const localVertex = new THREE.Vector3(
    positions[i],     // x
    positions[i + 1], // y
    positions[i + 2]  // z
  )
  
  // 轉(zhuǎn)換為世界坐標(biāo)
  const worldVertex = localVertex.clone()
  worldVertex.applyMatrix4(matrixWorld)
  
  worldVertices.push(worldVertex)
}

Step 2: 計(jì)算幾何體中心點(diǎn)(世界坐標(biāo))

// 方法1:直接使用mesh的世界位置(對(duì)于對(duì)稱幾何體)
const center = new THREE.Vector3()
mesh.getWorldPosition(center)

// 方法2:計(jì)算所有頂點(diǎn)的平均值(更精確)
let sum = new THREE.Vector3(0, 0, 0)
for (const vertex of worldVertices) {
  sum.add(vertex)
}
const center = sum.divideScalar(worldVertices.length)

Step 3: 創(chuàng)建頂點(diǎn)到中心的射線

// 3.1 創(chuàng)建Raycaster實(shí)例
const raycaster = new THREE.Raycaster()

// 3.2 對(duì)于每個(gè)頂點(diǎn),創(chuàng)建從頂點(diǎn)指向中心的射線
for (const vertex of worldVertices) {
  // 計(jì)算方向向量(從頂點(diǎn)指向中心)
  const direction = center.clone().sub(vertex).normalize()
  
  // 設(shè)置射線
  raycaster.set(vertex, direction)
  
  // 現(xiàn)在raycaster就代表了一條從頂點(diǎn)指向中心的射線
  // 我們可以用它來(lái)檢測(cè)與其他幾何體的交點(diǎn)
}

Step 4: 射線交叉計(jì)算

// 4.1 檢測(cè)射線是否與另一個(gè)幾何體相交
const otherMesh = ... // 另一個(gè)幾何體
const intersects = raycaster.intersectObject(otherMesh)

// 4.2 分析交點(diǎn)信息
if (intersects.length > 0) {
  const firstIntersect = intersects[0]  // 最近的交點(diǎn)
  
  // 交點(diǎn)信息包含:
  // - point: 交點(diǎn)坐標(biāo)(Vector3)
  // - distance: 從射線起點(diǎn)到交點(diǎn)的距離
  // - object: 被擊中的物體
  // - face: 被擊中的面
  // - faceIndex: 面的索引
}

Step 5: 通過(guò)距離判斷是否碰撞

// 5.1 計(jì)算關(guān)鍵距離
const distanceToIntersect = vertex.distanceTo(firstIntersect.point)  // 頂點(diǎn)到交點(diǎn)的距離
const distanceToCenter = vertex.distanceTo(center)                  // 頂點(diǎn)到中心的距離

// 5.2 判斷邏輯
if (distanceToIntersect < distanceToCenter) {
  // 情況:射線在到達(dá)自己中心之前就擊中了對(duì)方
  // 說(shuō)明對(duì)方幾何體在自己"前方"或"內(nèi)部"
  // 這很可能表示兩個(gè)幾何體相交或碰撞
  
  console.log('這條射線穿過(guò)了對(duì)方幾何體!')
  
  // 記錄碰撞信息
  collisions.push({
    vertex: vertex,
    intersectPoint: firstIntersect.point,
    rayDirection: direction
  })
} else {
  // 情況:射線先到達(dá)自己中心,然后才可能擊中對(duì)方
  // 說(shuō)明對(duì)方幾何體在自己"后方"或"外部"
  // 這很可能表示兩個(gè)幾何體沒(méi)有碰撞
}

Step 6: 綜合判斷碰撞

// 6.1 統(tǒng)計(jì)所有穿過(guò)的射線
let collisionCount = 0
for (所有頂點(diǎn)射線) {
  if (射線穿過(guò)了對(duì)方幾何體) {
    collisionCount++
  }
}

// 6.2 判斷是否發(fā)生碰撞
if (collisionCount > 0) {
  console.log(`發(fā)生碰撞!共有 ${collisionCount} 條射線穿過(guò)對(duì)方幾何體`)
  return true  // 發(fā)生碰撞
} else {
  console.log('沒(méi)有碰撞')
  return false  // 沒(méi)有碰撞
}

三、數(shù)學(xué)原理圖解

情況1:發(fā)生碰撞(射線穿過(guò)對(duì)方)
       頂點(diǎn)
        │
        │ 距離1(到交點(diǎn))
        │
        ▼ 交點(diǎn)(在對(duì)方幾何體上)
        │
        │ 距離2(從交點(diǎn)到中心)
        │
        ▼ 中心
       
       距離1 < 距離1+距離2(即距離1 < 頂點(diǎn)到中心的距離)
       ∴ 射線在到達(dá)中心之前擊中了對(duì)方 → 碰撞!

情況2:沒(méi)有碰撞(射線先到中心)
       頂點(diǎn)
        │
        │ 距離1(到中心)
        │
        ▼ 中心
        │
        │ 距離2(從中心到交點(diǎn))
        │
        ▼ 交點(diǎn)(在對(duì)方幾何體上)
       
       距離1 < 距離1+距離2(但射線要先經(jīng)過(guò)中心)
       ∴ 射線先到達(dá)中心,然后才可能擊中對(duì)方 → 沒(méi)有碰撞!

四、性能優(yōu)化考慮

1. 頂點(diǎn)采樣優(yōu)化

// 不檢測(cè)所有頂點(diǎn),只采樣一部分
const sampleRate = 0.3  // 采樣率30%
for (let i = 0; i < worldVertices.length; i += Math.floor(1/sampleRate)) {
  // 只檢測(cè)部分頂點(diǎn)
}

2. 分層次檢測(cè)

// 第一步:快速檢測(cè)(包圍盒)
const box1 = new THREE.Box3().setFromObject(mesh1)
const box2 = new THREE.Box3().setFromObject(mesh2)

if (!box1.intersectsBox(box2)) {
  return false  // 包圍盒不相交,肯定不碰撞,快速返回
}

// 第二步:精確檢測(cè)(頂點(diǎn)射線)
// 只有包圍盒相交時(shí),才進(jìn)行更耗時(shí)的頂點(diǎn)檢測(cè)

3. 空間分割優(yōu)化

// 使用八叉樹(shù)或BVH(包圍盒層次結(jié)構(gòu))加速
// 只檢測(cè)可能相交的幾何體

4. 距離閾值優(yōu)化

// 添加容差閾值
const threshold = 0.01  // 1厘米容差
if (distanceToIntersect < distanceToCenter + threshold) {
  // 考慮為碰撞(避免浮點(diǎn)數(shù)精度問(wèn)題)
}

五、適用場(chǎng)景與限制

適用場(chǎng)景:

  1. 需要精確碰撞檢測(cè)(如物理模擬、游戲碰撞)
  2. 不規(guī)則幾何體碰撞(非AABB/球體等簡(jiǎn)單形狀)
  3. 需要知道碰撞位置(不僅僅是是否碰撞)

限制:

  1. 計(jì)算量大:頂點(diǎn)越多越慢
  2. 凹形幾何體問(wèn)題:射線可能從凹處"穿過(guò)"而不碰撞
  3. 薄物體問(wèn)題:對(duì)于非常薄的幾何體可能漏檢

六、偽代碼總結(jié)

function 頂點(diǎn)射線碰撞檢測(cè)(幾何體A, 幾何體B):
    // Step 1: 獲取頂點(diǎn)
    頂點(diǎn)數(shù)組A = 獲取世界坐標(biāo)頂點(diǎn)(幾何體A)
    頂點(diǎn)數(shù)組B = 獲取世界坐標(biāo)頂點(diǎn)(幾何體B)
    
    // Step 2: 計(jì)算中心
    中心A = 計(jì)算世界中心(幾何體A)
    中心B = 計(jì)算世界中心(幾何體B)
    
    // Step 3-4: 檢測(cè)A的頂點(diǎn)射線
    for 每個(gè)頂點(diǎn)V in 頂點(diǎn)數(shù)組A:
        方向 = 歸一化(中心A - 頂點(diǎn)V)
        射線 = 創(chuàng)建射線(頂點(diǎn)V, 方向)
        交點(diǎn) = 射線.檢測(cè)相交(幾何體B)
        
        if 交點(diǎn)存在:
            if 距離(頂點(diǎn)V, 交點(diǎn)) < 距離(頂點(diǎn)V, 中心A):
                碰撞計(jì)數(shù)器++
    
    // Step 3-4: 檢測(cè)B的頂點(diǎn)射線
    for 每個(gè)頂點(diǎn)V in 頂點(diǎn)數(shù)組B:
        方向 = 歸一化(中心B - 頂點(diǎn)V)
        射線 = 創(chuàng)建射線(頂點(diǎn)V, 方向)
        交點(diǎn) = 射線.檢測(cè)相交(幾何體A)
        
        if 交點(diǎn)存在:
            if 距離(頂點(diǎn)V, 交點(diǎn)) < 距離(頂點(diǎn)V, 中心B):
                碰撞計(jì)數(shù)器++
    
    // Step 5: 判斷結(jié)果
    if 碰撞計(jì)數(shù)器 > 0:
        return true  // 發(fā)生碰撞
    else:
        return false // 沒(méi)有碰撞

—# Three.js 頂點(diǎn)射線碰撞檢測(cè)實(shí)現(xiàn)步驟詳解

一、基本思路

核心算法流程:

第1步:遍歷幾何體所有頂點(diǎn),分別創(chuàng)建與幾何體中心坐標(biāo)構(gòu)成的射線

對(duì)于 每個(gè)幾何體A 的 每個(gè)頂點(diǎn)V:
   頂點(diǎn)位置 = V 的世界坐標(biāo)位置
   中心位置 = 幾何體A 的世界坐標(biāo)中心點(diǎn)
   方向向量 = 中心位置 - 頂點(diǎn)位置
   
   創(chuàng)建射線Raycaster:
      起點(diǎn) = 頂點(diǎn)位置
      方向 = 方向向量(歸一化)

實(shí)現(xiàn)細(xì)節(jié):

  • 獲取幾何體的頂點(diǎn)坐標(biāo)數(shù)組(Float32Array,每3個(gè)值表示一個(gè)頂點(diǎn)的x,y,z坐標(biāo))
  • 將頂點(diǎn)從局部坐標(biāo)系轉(zhuǎn)換到世界坐標(biāo)系
  • 計(jì)算幾何體的世界中心點(diǎn)(通常是物體位置)
  • 對(duì)每個(gè)頂點(diǎn)創(chuàng)建從頂點(diǎn)指向中心的射線

第2步:射線交叉計(jì)算

對(duì)于 幾何體A 的 每條射線R:
   使用Raycaster的intersectObject()方法:
       檢測(cè)射線R 是否與 幾何體B 相交
       如果相交:
           獲取交點(diǎn)信息(交點(diǎn)坐標(biāo)、距離、面信息等)

關(guān)鍵點(diǎn):

  • Raycaster.intersectObject(幾何體B) 返回交點(diǎn)數(shù)組
  • 交點(diǎn)數(shù)組按距離從近到遠(yuǎn)排序
  • 如果射線與幾何體B有交點(diǎn),說(shuō)明這條射線可能穿過(guò)了幾何體B

第3步:通過(guò)距離判斷兩個(gè)網(wǎng)格模型是否碰撞

對(duì)于 幾何體A 的 每條射線R 的 每個(gè)交點(diǎn)I:
   計(jì)算:起點(diǎn)到交點(diǎn)的距離 = 距離1
   計(jì)算:起點(diǎn)到幾何體A中心的距離 = 距離2
   
   如果 距離1 < 距離2:
       說(shuō)明射線在到達(dá)自己中心之前就擊中了對(duì)方
       判斷為"這條射線穿過(guò)了對(duì)方幾何體"
   
   如果 至少有一條射線穿過(guò)對(duì)方幾何體:
       判斷為"兩個(gè)幾何體發(fā)生碰撞"

邏輯解釋:

  • 射線是從自己頂點(diǎn)指向自己中心的
  • 如果射線在到達(dá)自己中心之前就擊中了對(duì)方,說(shuō)明對(duì)方在自己"內(nèi)部"或"前方"
  • 如果射線先到達(dá)自己中心,說(shuō)明對(duì)方在自己"后方"或"外部"

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

Step 1: 獲取幾何體所有頂點(diǎn)(世界坐標(biāo))

// 1.1 獲取幾何體的頂點(diǎn)位置數(shù)據(jù)
const geometry = mesh.geometry
const positionAttribute = geometry.attributes.position
const positions = positionAttribute.array  // [x1, y1, z1, x2, y2, z2, ...]

// 1.2 獲取世界變換矩陣
const matrixWorld = mesh.matrixWorld

// 1.3 遍歷所有頂點(diǎn),轉(zhuǎn)換為世界坐標(biāo)
const worldVertices = []
for (let i = 0; i < positions.length; i += 3) {
  // 創(chuàng)建局部坐標(biāo)頂點(diǎn)
  const localVertex = new THREE.Vector3(
    positions[i],     // x
    positions[i + 1], // y
    positions[i + 2]  // z
  )
  
  // 轉(zhuǎn)換為世界坐標(biāo)
  const worldVertex = localVertex.clone()
  worldVertex.applyMatrix4(matrixWorld)
  
  worldVertices.push(worldVertex)
}

Step 2: 計(jì)算幾何體中心點(diǎn)(世界坐標(biāo))

// 方法1:直接使用mesh的世界位置(對(duì)于對(duì)稱幾何體)
const center = new THREE.Vector3()
mesh.getWorldPosition(center)

// 方法2:計(jì)算所有頂點(diǎn)的平均值(更精確)
let sum = new THREE.Vector3(0, 0, 0)
for (const vertex of worldVertices) {
  sum.add(vertex)
}
const center = sum.divideScalar(worldVertices.length)

Step 3: 創(chuàng)建頂點(diǎn)到中心的射線

// 3.1 創(chuàng)建Raycaster實(shí)例
const raycaster = new THREE.Raycaster()

// 3.2 對(duì)于每個(gè)頂點(diǎn),創(chuàng)建從頂點(diǎn)指向中心的射線
for (const vertex of worldVertices) {
  // 計(jì)算方向向量(從頂點(diǎn)指向中心)
  const direction = center.clone().sub(vertex).normalize()
  
  // 設(shè)置射線
  raycaster.set(vertex, direction)
  
  // 現(xiàn)在raycaster就代表了一條從頂點(diǎn)指向中心的射線
  // 我們可以用它來(lái)檢測(cè)與其他幾何體的交點(diǎn)
}

Step 4: 射線交叉計(jì)算

// 4.1 檢測(cè)射線是否與另一個(gè)幾何體相交
const otherMesh = ... // 另一個(gè)幾何體
const intersects = raycaster.intersectObject(otherMesh)

// 4.2 分析交點(diǎn)信息
if (intersects.length > 0) {
  const firstIntersect = intersects[0]  // 最近的交點(diǎn)
  
  // 交點(diǎn)信息包含:
  // - point: 交點(diǎn)坐標(biāo)(Vector3)
  // - distance: 從射線起點(diǎn)到交點(diǎn)的距離
  // - object: 被擊中的物體
  // - face: 被擊中的面
  // - faceIndex: 面的索引
}

Step 5: 通過(guò)距離判斷是否碰撞

// 5.1 計(jì)算關(guān)鍵距離
const distanceToIntersect = vertex.distanceTo(firstIntersect.point)  // 頂點(diǎn)到交點(diǎn)的距離
const distanceToCenter = vertex.distanceTo(center)                  // 頂點(diǎn)到中心的距離

// 5.2 判斷邏輯
if (distanceToIntersect < distanceToCenter) {
  // 情況:射線在到達(dá)自己中心之前就擊中了對(duì)方
  // 說(shuō)明對(duì)方幾何體在自己"前方"或"內(nèi)部"
  // 這很可能表示兩個(gè)幾何體相交或碰撞
  
  console.log('這條射線穿過(guò)了對(duì)方幾何體!')
  
  // 記錄碰撞信息
  collisions.push({
    vertex: vertex,
    intersectPoint: firstIntersect.point,
    rayDirection: direction
  })
} else {
  // 情況:射線先到達(dá)自己中心,然后才可能擊中對(duì)方
  // 說(shuō)明對(duì)方幾何體在自己"后方"或"外部"
  // 這很可能表示兩個(gè)幾何體沒(méi)有碰撞
}

Step 6: 綜合判斷碰撞

// 6.1 統(tǒng)計(jì)所有穿過(guò)的射線
let collisionCount = 0
for (所有頂點(diǎn)射線) {
  if (射線穿過(guò)了對(duì)方幾何體) {
    collisionCount++
  }
}

// 6.2 判斷是否發(fā)生碰撞
if (collisionCount > 0) {
  console.log(`發(fā)生碰撞!共有 ${collisionCount} 條射線穿過(guò)對(duì)方幾何體`)
  return true  // 發(fā)生碰撞
} else {
  console.log('沒(méi)有碰撞')
  return false  // 沒(méi)有碰撞
}

三、數(shù)學(xué)原理圖解

情況1:發(fā)生碰撞(射線穿過(guò)對(duì)方)
       頂點(diǎn)
        │
        │ 距離1(到交點(diǎn))
        │
        ▼ 交點(diǎn)(在對(duì)方幾何體上)
        │
        │ 距離2(從交點(diǎn)到中心)
        │
        ▼ 中心
       
       距離1 < 距離1+距離2(即距離1 < 頂點(diǎn)到中心的距離)
       ∴ 射線在到達(dá)中心之前擊中了對(duì)方 → 碰撞!

情況2:沒(méi)有碰撞(射線先到中心)
       頂點(diǎn)
        │
        │ 距離1(到中心)
        │
        ▼ 中心
        │
        │ 距離2(從中心到交點(diǎn))
        │
        ▼ 交點(diǎn)(在對(duì)方幾何體上)
       
       距離1 < 距離1+距離2(但射線要先經(jīng)過(guò)中心)
       ∴ 射線先到達(dá)中心,然后才可能擊中對(duì)方 → 沒(méi)有碰撞!

四、性能優(yōu)化考慮

1. 頂點(diǎn)采樣優(yōu)化

// 不檢測(cè)所有頂點(diǎn),只采樣一部分
const sampleRate = 0.3  // 采樣率30%
for (let i = 0; i < worldVertices.length; i += Math.floor(1/sampleRate)) {
  // 只檢測(cè)部分頂點(diǎn)
}

2. 分層次檢測(cè)

// 第一步:快速檢測(cè)(包圍盒)
const box1 = new THREE.Box3().setFromObject(mesh1)
const box2 = new THREE.Box3().setFromObject(mesh2)

if (!box1.intersectsBox(box2)) {
  return false  // 包圍盒不相交,肯定不碰撞,快速返回
}

// 第二步:精確檢測(cè)(頂點(diǎn)射線)
// 只有包圍盒相交時(shí),才進(jìn)行更耗時(shí)的頂點(diǎn)檢測(cè)

3. 空間分割優(yōu)化

// 使用八叉樹(shù)或BVH(包圍盒層次結(jié)構(gòu))加速
// 只檢測(cè)可能相交的幾何體

4. 距離閾值優(yōu)化

// 添加容差閾值
const threshold = 0.01  // 1厘米容差
if (distanceToIntersect < distanceToCenter + threshold) {
  // 考慮為碰撞(避免浮點(diǎn)數(shù)精度問(wèn)題)
}

五、適用場(chǎng)景與限制

適用場(chǎng)景:

  1. 需要精確碰撞檢測(cè)(如物理模擬、游戲碰撞)
  2. 不規(guī)則幾何體碰撞(非AABB/球體等簡(jiǎn)單形狀)
  3. 需要知道碰撞位置(不僅僅是是否碰撞)

限制:

  1. 計(jì)算量大:頂點(diǎn)越多越慢
  2. 凹形幾何體問(wèn)題:射線可能從凹處"穿過(guò)"而不碰撞
  3. 薄物體問(wèn)題:對(duì)于非常薄的幾何體可能漏檢

六、偽代碼總結(jié)

function 頂點(diǎn)射線碰撞檢測(cè)(幾何體A, 幾何體B):
    // Step 1: 獲取頂點(diǎn)
    頂點(diǎn)數(shù)組A = 獲取世界坐標(biāo)頂點(diǎn)(幾何體A)
    頂點(diǎn)數(shù)組B = 獲取世界坐標(biāo)頂點(diǎn)(幾何體B)
    
    // Step 2: 計(jì)算中心
    中心A = 計(jì)算世界中心(幾何體A)
    中心B = 計(jì)算世界中心(幾何體B)
    
    // Step 3-4: 檢測(cè)A的頂點(diǎn)射線
    for 每個(gè)頂點(diǎn)V in 頂點(diǎn)數(shù)組A:
        方向 = 歸一化(中心A - 頂點(diǎn)V)
        射線 = 創(chuàng)建射線(頂點(diǎn)V, 方向)
        交點(diǎn) = 射線.檢測(cè)相交(幾何體B)
        
        if 交點(diǎn)存在:
            if 距離(頂點(diǎn)V, 交點(diǎn)) < 距離(頂點(diǎn)V, 中心A):
                碰撞計(jì)數(shù)器++
    
    // Step 3-4: 檢測(cè)B的頂點(diǎn)射線
    for 每個(gè)頂點(diǎn)V in 頂點(diǎn)數(shù)組B:
        方向 = 歸一化(中心B - 頂點(diǎn)V)
        射線 = 創(chuàng)建射線(頂點(diǎn)V, 方向)
        交點(diǎn) = 射線.檢測(cè)相交(幾何體A)
        
        if 交點(diǎn)存在:
            if 距離(頂點(diǎn)V, 交點(diǎn)) < 距離(頂點(diǎn)V, 中心B):
                碰撞計(jì)數(shù)器++
    
    // Step 5: 判斷結(jié)果
    if 碰撞計(jì)數(shù)器 > 0:
        return true  // 發(fā)生碰撞
    else:
        return false // 沒(méi)有碰撞

這個(gè)算法的主要思想是:如果一個(gè)幾何體的頂點(diǎn)發(fā)出的、指向自己中心的射線,在到達(dá)中心之前就擊中了另一個(gè)幾何體,那么這兩個(gè)幾何體很可能發(fā)生了碰撞或相交。

這個(gè)算法的主要思想是:如果一個(gè)幾何體的頂點(diǎn)發(fā)出的、指向自己中心的射線,在到達(dá)中心之前就擊中了另一個(gè)幾何體,那么這兩個(gè)幾何體很可能發(fā)生了碰撞或相交。

<template>
  <div ref="containerRef"></div>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import * as THREE from 'three'

const containerRef = ref(null)
let cube1, cube2

onMounted(() => {
  const scene = new THREE.Scene()
  scene.background = new THREE.Color(0x111111)
  
  const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.set(0, 5, 10)
  
  const renderer = new THREE.WebGLRenderer()
  renderer.setSize(window.innerWidth, window.innerHeight)
  containerRef.value.appendChild(renderer.domElement)
  
  // 創(chuàng)建立方體1(可移動(dòng))
  cube1 = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
  )
  cube1.position.set(-3, 0, 0)
  scene.add(cube1)
  
  // 創(chuàng)建立方體2(靜止)
  cube2 = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true })
  )
  cube2.position.set(0, 0, 0)
  scene.add(cube2)
  
  // 輔助線
  scene.add(new THREE.AxesHelper(5))
  
  // 核心算法:頂點(diǎn)射線碰撞檢測(cè)
  const checkVertexRayCollision = () => {
    console.log('=== 頂點(diǎn)射線碰撞檢測(cè) ===')
    
    // 1. 獲取頂點(diǎn)(世界坐標(biāo))
    const getVertices = (mesh) => {
      const geometry = mesh.geometry
      const positions = geometry.attributes.position.array
      const vertices = []
      const matrixWorld = mesh.matrixWorld
      
      for (let i = 0; i < positions.length; i += 3) {
        const vertex = new THREE.Vector3(
          positions[i],
          positions[i + 1],
          positions[i + 2]
        )
        vertex.applyMatrix4(matrixWorld)
        vertices.push(vertex)
      }
      return vertices
    }
    
    const vertices1 = getVertices(cube1)
    const vertices2 = getVertices(cube2)
    
    // 2. 獲取中心點(diǎn)(世界坐標(biāo))
    const center1 = new THREE.Vector3()
    cube1.getWorldPosition(center1)
    
    const center2 = new THREE.Vector3()
    cube2.getWorldPosition(center2)
    
    console.log('立方體1頂點(diǎn)數(shù):', vertices1.length)
    console.log('立方體2頂點(diǎn)數(shù):', vertices2.length)
    console.log('立方體1中心:', center1)
    console.log('立方體2中心:', center2)
    
    // 3. 檢測(cè)立方體1的頂點(diǎn)射線
    let collisionCount = 0
    
    for (let i = 0; i < vertices1.length; i++) {
      const vertex = vertices1[i]
      const direction = new THREE.Vector3().subVectors(center1, vertex).normalize()
      
      const raycaster = new THREE.Raycaster()
      raycaster.set(vertex, direction)
      
      const intersects = raycaster.intersectObject(cube2)
      
      if (intersects.length > 0) {
        const intersect = intersects[0]
        const distanceToHit = vertex.distanceTo(intersect.point)
        const distanceToCenter = vertex.distanceTo(center1)
        
        if (distanceToHit < distanceToCenter) {
          collisionCount++
          console.log(`立方體1頂點(diǎn)${i}: 射線穿過(guò)了立方體2`)
        }
      }
    }
    
    // 4. 檢測(cè)立方體2的頂點(diǎn)射線
    for (let i = 0; i < vertices2.length; i++) {
      const vertex = vertices2[i]
      const direction = new THREE.Vector3().subVectors(center2, vertex).normalize()
      
      const raycaster = new THREE.Raycaster()
      raycaster.set(vertex, direction)
      
      const intersects = raycaster.intersectObject(cube1)
      
      if (intersects.length > 0) {
        const intersect = intersects[0]
        const distanceToHit = vertex.distanceTo(intersect.point)
        const distanceToCenter = vertex.distanceTo(center2)
        
        if (distanceToHit < distanceToCenter) {
          collisionCount++
          console.log(`立方體2頂點(diǎn)${i}: 射線穿過(guò)了立方體1`)
        }
      }
    }
    
    // 5. 判斷結(jié)果
    if (collisionCount > 0) {
      console.log(`?? 發(fā)生碰撞!共有 ${collisionCount} 條射線相交`)
      cube1.material.color.set(0xff00ff)
      cube2.material.color.set(0xff00ff)
      return true
    } else {
      console.log('? 沒(méi)有碰撞')
      cube1.material.color.set(0xff0000)
      cube2.material.color.set(0x0000ff)
      return false
    }
  }
  
  // 自動(dòng)移動(dòng)并檢測(cè)
  let direction = 1
  const animate = () => {
    requestAnimationFrame(animate)
    
    // 移動(dòng)立方體1
    cube1.position.x += 0.01 * direction
    
    // 邊界檢測(cè)
    if (cube1.position.x > 2) direction = -1
    if (cube1.position.x < -4) direction = 1
    
    // 每10幀檢測(cè)一次
    if (Math.floor(cube1.position.x * 10) % 10 === 0) {
      checkVertexRayCollision()
    }
    
    renderer.render(scene, camera)
  }
  animate()
})
</script>

<style scoped>
div {
  width: 100vw;
  height: 100vh;
}
</style>

總結(jié) 

到此這篇關(guān)于Three.js頂點(diǎn)射線碰撞檢測(cè)具體實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)Three.js頂點(diǎn)射線碰撞檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Bootstrap中data-target 到底是什么

    Bootstrap中data-target 到底是什么

    這篇文章主要介紹了Bootstrap中data-target 到底是什么的相關(guān)資料,我目前理解到在bootstrap中data-target,data-toggle等屬性主要有兩種作用,具體哪兩種作用,大家通過(guò)本文詳細(xì)了解下
    2017-02-02
  • JavaScript?Object.defineProperty與proxy代理模式的使用詳細(xì)分析

    JavaScript?Object.defineProperty與proxy代理模式的使用詳細(xì)分析

    這篇文章主要介紹了JavaScript?Object.defineProperty與proxy代理模式的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-10-10
  • JS+DIV+CSS排版布局實(shí)現(xiàn)美觀的選項(xiàng)卡效果

    JS+DIV+CSS排版布局實(shí)現(xiàn)美觀的選項(xiàng)卡效果

    這篇文章主要介紹了JS+DIV+CSS排版布局實(shí)現(xiàn)美觀的選項(xiàng)卡效果,通過(guò)簡(jiǎn)單的div+css布局結(jié)合JavaScript切換頁(yè)面樣式實(shí)現(xiàn)美觀的選項(xiàng)卡效果,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-10-10
  • js數(shù)組案例之五子棋游戲

    js數(shù)組案例之五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了js數(shù)組案例之五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 談?wù)凧avaScript數(shù)組常用方法總結(jié)

    談?wù)凧avaScript數(shù)組常用方法總結(jié)

    本篇文章主要介紹了談?wù)凧avaScript數(shù)組常用方法總結(jié),在JavaScript中,我們需要時(shí)常對(duì)數(shù)組進(jìn)行操作。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • 小程序富文本提取圖片可放大縮小

    小程序富文本提取圖片可放大縮小

    這篇文章主要為大家詳細(xì)介紹了小程序富文本提取圖片可放大縮小,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • JavaScript實(shí)現(xiàn)英語(yǔ)單詞題庫(kù)

    JavaScript實(shí)現(xiàn)英語(yǔ)單詞題庫(kù)

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)英語(yǔ)單詞題庫(kù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • JavaScript中URL.createObjectURL和Blob實(shí)現(xiàn)保存文件

    JavaScript中URL.createObjectURL和Blob實(shí)現(xiàn)保存文件

    本文介紹了利用JavaScript中的Blob對(duì)象和URL.createObjectURL方法在瀏覽器端創(chuàng)建文件并實(shí)現(xiàn)下載功能,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • Layui 設(shè)置select下拉框自動(dòng)選中某項(xiàng)的方法

    Layui 設(shè)置select下拉框自動(dòng)選中某項(xiàng)的方法

    今天小編就為大家分享一篇Layui 設(shè)置select下拉框自動(dòng)選中某項(xiàng)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • typescript中type和interface的區(qū)別有哪些

    typescript中type和interface的區(qū)別有哪些

    大家使用typescript總會(huì)使用到interface和type,所以下面這篇文章主要給大家介紹了關(guān)于typescript中type和interface區(qū)別的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02

最新評(píng)論

介休市| 洛宁县| 南城县| 常熟市| 永嘉县| 永顺县| 嘉禾县| 密云县| 宜丰县| 高台县| 五寨县| 陕西省| 长宁县| 清河县| 三原县| 岗巴县| 开化县| 灵川县| 吉隆县| 清水河县| 江阴市| 巴塘县| 萍乡市| 宁化县| 长泰县| 乾安县| 新密市| 青田县| 玉林市| 儋州市| 山东省| 甘肃省| 清镇市| 东平县| 博湖县| 舒兰市| 鹤壁市| 杭锦后旗| 宁国市| 桐乡市| 昭觉县|