1. 引言
Three.js中,Box3物件指的是AABB式的包圍盒,這種包圍盒會隨物體的旋轉而變換大小,精度較差
Three.js中還有OBB物件,這是一種能表現物體主要特徵的、不隨物體的旋轉而變換大小的包圍盒
兩者如下圖所示:
Three.js中雖然有OBB,卻沒有OBB Helper,即OBB包圍盒線框物件
本文參考Box3Helper原始碼,並寫出一個OBBHelper
2. Box3Helper
以下是Three.js原始碼中的Box3Helper:
import { LineSegments } from '../objects/LineSegments.js';
import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
import { BufferAttribute, Float32BufferAttribute } from '../core/BufferAttribute.js';
import { BufferGeometry } from '../core/BufferGeometry.js';
class Box3Helper extends LineSegments {
constructor( box, color = 0xffff00 ) {
const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];
const geometry = new BufferGeometry();
geometry.setIndex( new BufferAttribute( indices, 1 ) );
geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
this.box = box;
this.type = 'Box3Helper';
this.geometry.computeBoundingSphere();
}
updateMatrixWorld( force ) {
const box = this.box;
if ( box.isEmpty() ) return;
box.getCenter( this.position );
box.getSize( this.scale );
this.scale.multiplyScalar( 0.5 );
super.updateMatrixWorld( force );
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}
export { Box3Helper };
這段程式碼是一個名為Box3Helper
的類的定義,它繼承自LineSegments
類。Box3Helper
類用於建立一個輔助框,用來視覺化Box3
物件的邊界框。
程式碼中首先匯入了一些依賴的模組,包括LineSegments
、LineBasicMaterial
、BufferAttribute
、Float32BufferAttribute
和BufferGeometry
。
在Box3Helper
類的建構函式中,首先建立了一個表示邊界框的索引陣列indices
,然後建立了一個表示邊界框的頂點座標陣列positions
。
接下來,建立了一個BufferGeometry
物件,並使用indices
陣列建立了一個BufferAttribute
物件來表示索引,使用positions
陣列建立了一個Float32BufferAttribute
物件來表示頂點座標。然後將這兩個屬性設定到geometry
物件中。
然後呼叫父類LineSegments
的建構函式,傳入geometry
和一個LineBasicMaterial
物件作為引數,來建立一個視覺化邊界框的線段物件。
接著,將傳入建構函式的box
引數賦值給this.box
屬性。
然後設定this.type
屬性為'Box3Helper'
。
最後呼叫geometry
物件的computeBoundingSphere
方法來計算邊界球。
Box3Helper
類還定義了一個updateMatrixWorld
方法,用於更新輔助框的世界矩陣。在該方法中,首先獲取this.box
的中心點和尺寸,然後根據尺寸縮放輔助框的比例,並呼叫父類的updateMatrixWorld
方法來更新世界矩陣。
最後,定義了一個dispose
方法,用於釋放資源,包括釋放geometry
和material
物件。
最後透過export
語句將Box3Helper
類匯出,以便在其他地方使用。
3. OBBHelper
參考上面的程式碼。給出OBBHelper的程式碼如下:
import {
Vector3, LineSegments, LineBasicMaterial,
BufferAttribute, Float32BufferAttribute, BufferGeometry
} from 'three';
class OBBHelper extends LineSegments {
constructor(obb, object, color = 0xffff00) {
const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7, 0, 2, 1, 3, 4, 6, 5, 7]);
const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];
const geometry = new BufferGeometry();
geometry.setIndex(new BufferAttribute(indices, 1));
geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
super(geometry, new LineBasicMaterial({ color: color, toneMapped: false }));
this.obb = obb;
this.object = object;
this.type = 'OBBHelper';
this.lastMatrix4 = object.matrixWorld.clone();
}
updateMatrixWorld(force) {
this.obb.applyMatrix4(this.lastMatrix4.invert())
this.obb.applyMatrix4(this.object.matrixWorld);
this.lastMatrix4 = this.object.matrixWorld.clone();
const positions = this.geometry.attributes.position.array;
const halfSize = this.obb.halfSize;
const center = this.obb.center;
const rotation = this.obb.rotation;
const corners = [];
for (let i = 0; i < 8; i++) {
const corner = new Vector3();
corner.x = (i & 1) ? center.x + halfSize.x : center.x - halfSize.x;
corner.y = (i & 2) ? center.y + halfSize.y : center.y - halfSize.y;
corner.z = (i & 4) ? center.z + halfSize.z : center.z - halfSize.z;
corner.applyMatrix3(rotation);
corners.push(corner);
}
for (let i = 0; i < corners.length; i++) {
const corner = corners[i];
positions[i * 3] = corner.x;
positions[i * 3 + 1] = corner.y;
positions[i * 3 + 2] = corner.z;
}
this.geometry.attributes.position.needsUpdate = true;
super.updateMatrixWorld(force);
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}
export { OBBHelper };
這段程式碼是一個自定義的 OBBHelper
類,用於建立一個輔助物件來顯示一個方向包圍盒(OBB)的邊界框。以下是程式碼的解釋:
-
匯入了所需的 Three.js 模組和類。這些模組和類包括
Vector3
、LineSegments
、LineBasicMaterial
、BufferAttribute
、Float32BufferAttribute
和BufferGeometry
。 -
OBBHelper
類繼承自LineSegments
類,因此它是一個線段物件。 -
OBBHelper
建構函式接收三個引數:obb
、object
和color
。obb
是一個方向包圍盒物件,object
是一個 Three.js 物件,color
是邊界框的顏色,預設為黃色(0xffff00)。 -
建立一個
indices
陣列,其中包含了邊界框的頂點索引。這些索引指定了邊界框的邊的連線關係。 -
建立一個
positions
陣列,其中包含了邊界框的頂點位置。這些位置定義了邊界框的形狀。 -
建立一個
BufferGeometry
物件,用於儲存幾何資料。 -
使用
geometry.setIndex
方法將索引資料分配給幾何體的索引屬性。 -
使用
geometry.setAttribute
方法將頂點位置資料分配給幾何體的位置屬性。 -
呼叫父類
LineSegments
的建構函式,傳遞幾何體和材質作為引數,建立一個線段物件。 -
設定
OBBHelper
物件的屬性,包括obb
、object
和type
。 -
在
updateMatrixWorld
方法中,更新輔助物件的世界矩陣。首先,將上一次的世界矩陣的逆矩陣應用於obb
物件,然後將當前的世界矩陣應用於obb
物件。接著,根據obb
物件的屬性計算出邊界框的頂點位置,並更新幾何體的位置屬性。 -
最後,呼叫父類的
updateMatrixWorld
方法,更新輔助物件的世界矩陣。 -
dispose
方法用於釋放幾何體和材質的記憶體。 -
匯出
OBBHelper
類供其他模組使用。
透過使用這個 OBBHelper
類,可以建立一個輔助物件來顯示一個方向包圍盒的邊界框,並將其新增到場景中以進行渲染和顯示。
實現的效果如下(黃色為Box3Helper,紅色為OBBHelper):
4. 參考資料
[1] OBB – three.js docs (three3d.cn)
[2] three.js/src/helpers/Box3Helper.js at master · mrdoob/three.js (github.com)
[3] three.js examples (three3d.cn)
[4] three.js/examples/jsm/math/OBB.js at master · mrdoob/three.js (github.com)
[5] BufferGeometry.boundingBox的應用:BoxHelper的實現 - 掘金 (juejin.cn)
[6] 113 Three.js的obb (OrientedboundingBox)方向包圍盒的使用_暮志未晚Webgl的部落格-CSDN部落格