Three.js中实现一个OBBHelper

1. 引言

Three.js中,Box3对象指的是AABB式的包围盒,这种包围盒会随物体的旋转而变换大小,精度较差

Three.js中还有OBB对象,这是一种能表现物体主要特征的、不随物体的旋转而变换大小的包围盒

两者如下图所示:

Three.js中虽然有OBB,却没有OBB Helper,即OBB包围盒线框对象

本文参考Box3Helper源码,并写出一个OBBHelper

2. Box3Helper

以下是Three.js源码中的Box3Helper:

javascript 复制代码
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对象的边界框。

代码中首先导入了一些依赖的模块,包括LineSegmentsLineBasicMaterialBufferAttributeFloat32BufferAttributeBufferGeometry

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方法,用于释放资源,包括释放geometrymaterial对象。

最后通过export语句将Box3Helper类导出,以便在其他地方使用。

3. OBBHelper

参考上面的代码。给出OBBHelper的代码如下:

javascript 复制代码
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)的边界框。以下是代码的解释:

  1. 导入了所需的 Three.js 模块和类。这些模块和类包括 Vector3LineSegmentsLineBasicMaterialBufferAttributeFloat32BufferAttributeBufferGeometry

  2. OBBHelper 类继承自 LineSegments 类,因此它是一个线段对象。

  3. OBBHelper 构造函数接收三个参数:obbobjectcolorobb 是一个方向包围盒对象,object 是一个 Three.js 对象,color 是边界框的颜色,默认为黄色(0xffff00)。

  4. 创建一个 indices 数组,其中包含了边界框的顶点索引。这些索引指定了边界框的边的连接关系。

  5. 创建一个 positions 数组,其中包含了边界框的顶点位置。这些位置定义了边界框的形状。

  6. 创建一个 BufferGeometry 对象,用于存储几何数据。

  7. 使用 geometry.setIndex 方法将索引数据分配给几何体的索引属性。

  8. 使用 geometry.setAttribute 方法将顶点位置数据分配给几何体的位置属性。

  9. 调用父类 LineSegments 的构造函数,传递几何体和材质作为参数,创建一个线段对象。

  10. 设置 OBBHelper 对象的属性,包括 obbobjecttype

  11. updateMatrixWorld 方法中,更新辅助对象的世界矩阵。首先,将上一次的世界矩阵的逆矩阵应用于 obb 对象,然后将当前的世界矩阵应用于 obb 对象。接着,根据 obb 对象的属性计算出边界框的顶点位置,并更新几何体的位置属性。

  12. 最后,调用父类的 updateMatrixWorld 方法,更新辅助对象的世界矩阵。

  13. dispose 方法用于释放几何体和材质的内存。

  14. 导出 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博客

相关推荐
郝学胜-神的一滴3 天前
[简化版 GAMES 101] 计算机图形学 12:可见性与 Z‑Buffer 深度缓存
unity·godot·图形渲染·three.js·opengl·unreal
VcB之殇4 天前
[Three.js] 实现两个3D模型之间的粒子化切换
前端·javascript·three.js
郝学胜-神的一滴6 天前
中级OpenGL教程 008:精准控制高光光斑大小与强度
c++·unity·godot·three.js·图形学·opengl·unreal
xier1234569 天前
three-instance-batch 开发笔记
javascript·three.js
一根数据线11 天前
从几何压缩到KTX2纹理压缩:轻装3D的Three.js场景优化进阶
3d模型轻量化·three.js·3d模型·ktx2·轻装3d·纹理压缩
一根数据线13 天前
一键解决ThreeJS3D场景卡顿问题!轻装3D的几何体实例化与合并
3d模型轻量化·three.js·3d模型·轻装3d·实例化渲染·几何体合并
一根数据线13 天前
ThreeJS模型加载卡顿怎么办,用轻装3D来做模型压缩和LOD分级
3d模型轻量化·three.js·lod·3d模型优化·draco压缩·轻装3d
来自上海的这位朋友13 天前
用 Three.js 做一个 Web 3D 非对称追猎 Demo:从场景、角色到手感调试
后端·游戏开发·three.js
来自上海的这位朋友13 天前
Spring Boot + MySQL 搭一个多人游戏后端:登录、房间、匹配、对局和成长系统
前端·后端·three.js
郝学胜-神的一滴14 天前
中级OpenGL教程 007:解决背面光照异常高光问题
c++·unity·游戏引擎·three.js·opengl·unreal