定向包围盒(OBB,Oriented Bounding Box) 是一种三维包围盒,它与物体的旋转方向对齐,而不是像轴对齐包围盒(AABB)那样总是与坐标轴对齐。OBB 的优势在于它能够更紧密地包围物体,从而减少包围盒的空白区域。
OBB 通过 applyMatrix4 方法获取物体的边界信息以使用于包围盒计算
OBB 有三个属性十五个方法
OBB( center : Vector3, halfSize : Vector3, rotation : Matrix3 )
center — OBB 的中心。(可选)
halfSize — OBB 沿每个轴的正半宽范围。(可选)
rotation — OBB 的旋转。(可选)
创建一个新的 OBB。
以下为代码示例
// 创建 WebGL 渲染器,启用抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置渲染器的大小
renderer.setSize( width, height );
// 启用局部裁剪
renderer.localClippingEnabled = true;
// 启用阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 设置阴影类型为软阴影
// 将渲染器的 DOM 元素添加到页面中
DOMEl.appendChild( renderer.domElement );
// 创建场景对象
const scene = new THREE.Scene();
// 设置清空背景色为黑色
renderer.setClearColor(0x000000);
// 创建透视相机
const camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );
// 设置相机位置
camera.position.set( 0, 80, 0 );
// 让相机指向场景的中心
camera.lookAt( 0, 0, 0 );
// 创建时钟对象用于计算每帧的时间差
const clock = new THREE.Clock();
// 存储物体的数组
const objects = [];
// 存储鼠标坐标的向量
const mouse = new THREE.Vector2();
// 创建射线投射器
const raycaster = new THREE.Raycaster();
// 创建半球光源,模拟环境光
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x222222, 4 );
hemiLight.position.set( 1, 1, 1 );
// 将半球光源添加到场景中
scene.add( hemiLight );
// 创建盒子的尺寸
const size = new THREE.Vector3( 10, 5, 6 );
// 创建盒子几何体
const geometry = new THREE.BoxGeometry( size.x, size.y, size.z );
// 为几何体添加用户数据,存储 OBB 信息
geometry.userData.obb = new OBB();
geometry.userData.obb.halfSize.copy( size ).multiplyScalar( 0.5 );
// 创建多个物体并添加到场景中
for ( let i = 0; i < 100; i ++ ) {
const object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0x00ff00 } ) );
object.matrixAutoUpdate = false;
object.position.x = Math.random() * 80 - 40;
object.position.y = Math.random() * 80 - 40;
object.position.z = Math.random() * 80 - 40;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add( object );
// 为物体添加 OBB 信息
object.userData.obb = new OBB();
objects.push( object );
};
// 创建一个可视化的碰撞框
const hitbox = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true } ) );
// 鼠标点击事件的回调函数
function onClick( event ) {
event.preventDefault();
const rect = DOMEl.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / height) * 2 + 1;
// 根据鼠标坐标生成射线
raycaster.setFromCamera( mouse, camera );
const intersectionPoint = new THREE.Vector3();
const intersections = [];
// 遍历所有物体,检测射线是否与物体的 OBB 相交
for ( let i = 0, il = objects.length; i < il; i ++ ) {
const object = objects[ i ];
const obb = object.userData.obb;
const ray = raycaster.ray;
if ( obb.intersectRay( ray, intersectionPoint ) !== null ) {
const distance = ray.origin.distanceTo( intersectionPoint );
intersections.push( { distance: distance, object: object } );
}
};
console.log(intersections,"intersections")
// 如果有相交的物体,将第一个相交物体添加碰撞框
if ( intersections.length > 0 ) {
intersections.sort( sortIntersections );
intersections[ 0 ].object.add( hitbox );
} else {
// 如果没有相交物体,从场景中移除碰撞框
const parent = hitbox.parent;
if ( parent ) parent.remove( hitbox );
}
}
// 排序函数,按照距离升序排列
function sortIntersections( a, b ) {
return a.distance - b.distance;
}
// 添加点击事件监听器
DOMEl.addEventListener( 'click', onClick );
// 创建 OrbitControls 实例,用于控制相机
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器的目标点
controls.target.set(0, 0, 0); // 将目标点设置为场景的中心
// 动画函数
function animate() {
requestAnimationFrame(animate);
controls.update(); // 更新 OrbitControls
const delta = clock.getDelta();
// 更新每个物体的旋转
for ( let i = 0, il = objects.length; i < il; i ++ ) {
const object = objects[ i ];
object.rotation.x += delta * Math.PI * 0.20;
object.rotation.y += delta * Math.PI * 0.1;
object.updateMatrix();
object.updateMatrixWorld();
// 更新物体的 OBB 信息
object.userData.obb.copy( object.geometry.userData.obb );
object.userData.obb.applyMatrix4( object.matrixWorld );
object.material.color.setHex( 0x00ff00 );
}
// 检查物体之间的碰撞
for ( let i = 0, il = objects.length; i < il; i ++ ) {
const object = objects[ i ];
const obb = object.userData.obb;
for ( let j = i + 1, jl = objects.length; j < jl; j ++ ) {
const objectToTest = objects[ j ];
const obbToTest = objectToTest.userData.obb;
// 如果物体之间发生碰撞,改变它们的颜色
if ( obb.intersectsOBB( obbToTest ) === true ) {
object.material.color.setHex( 0xff0000 );
objectToTest.material.color.setHex( 0xff0000 );
}
}
}
// 渲染场景
renderer.render(scene, camera);
}
// 启动动画循环
animate();
属性
- center : Vector3 OBB 的中心。默认值为 ( 0, 0, 0 )。
- halfSize : Vector3 OBB 沿每个轴的正半宽范围。默认值为 ( 0, 0, 0 )。
- rotation : Matrix3 OBB 的旋转。默认为单位矩阵。
方法
- applyMatrix4 ( matrix : Matrix4 ) : this matrix — 一个 4x4 变换矩阵。 将给定的变换矩阵应用于此 OBB。此方法可用于将包围体与 3D 对象的世界矩阵进行转换,以保持两个实体同步。
- clampPoint ( point : Vector3, clampedPoint : Vector3 ) : Vector3 point — 应该被限制在该 OBB 范围内的点。 clampedPoint — 结果将被复制到此向量中。 将给定点限制在该 OBB 范围内。
- clone () : OBB 创建此实例的克隆。
- containsPoint ( point : Vector3 ) : Boolean point — 要测试的点。 给定点是否位于此 OBB 内。
- copy ( obb : OBB ) : this obb — 要复制的 OBB。 将给定 OBB 的属性复制到此 OBB。
- equals ( obb : OBB ) : Boolean obb — 要测试的 OBB。 给定的 OBB 是否等于此 OBB。
- fromBox3 ( box3 : Box3 ) : this box3 — AABB 根据给定的 AABB 定义 OBB。
- getSize ( size : Vector3 ) : Vector3 size — 结果将被复制到此向量中。 将此 OBB 的大小返回到给定向量中。
- intersectsBox3 ( box3 : Box3 ) : Boolean box3 — 要测试的 box3。 给定的 box3 是否与此 OBB 相交。
- intersectsSphere ( sphere : Sphere ) : Boolean sphere — 要测试的边界球体。 给定的边界球体是否与此 OBB 相交。
- intersectsOBB ( obb : OBB, epsilon : Number ) : Boolean obb — 要测试的 OBB epsilon — 一个可选的数值,用于抵消算术错误。默认为 Number.EPSILON。 给定的 OBB 是否与此 OBB 相交。
- intersectsPlane ( plane : Plane ) : Boolean plane — 要测试的平面。 给定平面是否与此 OBB 相交。
- intersectRay ( ray : Ray ) : Boolean ray — 要测试的射线。 给定射线是否与此 OBB 相交。
- intersectRay ( ray : Ray, intersectionPoint : Vector3 ) : Vector3 ray — 要测试的射线。 intersectionPoint — 结果将被复制到此向量中。 执行 射线/OBB 相交测试并将相交点存储到给定的 3D 向量。如果没有检测到交叉点则返回 null。
- set ( center : Vector3, halfSize : Vector3, rotation : Matrix3 ) : this center — OBB 的中心。 halfSize — OBB 沿每个轴的正半宽范围。 rotation — OBB 的旋转。 定义给定值的 OBB。
总结
- OBB(有序包围盒):用于提供物体的边界信息,尤其在物体有旋转、缩放和位置变换时,能够提供更精确的碰撞检测和空间计算。
- 与几何体的关系:OBB 并不直接包含物体的几何体,它依赖物体的几何尺寸和形状来创建一个包围盒(bounding box)。这个包围盒会根据物体的变换(位置、旋转、缩放)进行更新。
- 如何更新边界信息
- OBB 通过应用物体的 变换矩阵(matrixWorld)来更新包围盒的位置和尺寸,使其能够正确反映物体在世界坐标系中的实际状态。
- 每当物体的变换发生变化时,OBB 会重新计算和更新包围盒。
- OBB 的作用:OBB 是为物体提供一个精确的边界框,帮助进行碰撞检测、光线交集等空间计算,而不需要直接操作物体的几何体。
关键点: - OBB 使用矩阵更新边界信息,确保包围盒随着物体的变换而更新。 - OBB 不需要直接包含几何体,而是通过物体的几何尺寸、位置、旋转等信息来计算包围盒。