- 作者:老汪软件技巧
- 发表时间:2024-11-15 11:02
- 浏览量:
定向包围盒(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();
属性
方法
总结
OBB(有序包围盒):用于提供物体的边界信息,尤其在物体有旋转、缩放和位置变换时,能够提供更精确的碰撞检测和空间计算。与几何体的关系:OBB 并不直接包含物体的几何体,它依赖物体的几何尺寸和形状来创建一个包围盒(bounding box)。这个包围盒会根据物体的变换(位置、旋转、缩放)进行更新。如何更新边界信息OBB 的作用:OBB 是为物体提供一个精确的边界框,帮助进行碰撞检测、光线交集等空间计算,而不需要直接操作物体的几何体。
关键点:- OBB 使用矩阵更新边界信息,确保包围盒随着物体的变换而更新。- OBB 不需要直接包含几何体,而是通过物体的几何尺寸、位置、旋转等信息来计算包围盒。