对象的准确边界框
Accurate bounding box of an object
我正在尝试创建对象的精确边界框,但似乎如果对象未与轴对齐(我认为),则框未与对象对齐。
例如:
粉红色和靠近橙色的顶点是这面墙的 Box3.min、Box3.max,但是你看那面墙上没有红色、绿色和蓝色。您可以忽略水绿色顶点。
这是创建边界框(returns Box3)的代码:
static getWorldBoundingBox(model, dbId) {
return new Promise(async(resolve, reject)=>{
try{
var fragIds = await ViewerToolkit.getFragIds(
model, dbId);
if(!fragIds.length){
return reject('No geometry, invalid dbId?');
}
var fragList = model.getFragmentList();
var fragbBox = new THREE.Box3();
var nodebBox = new THREE.Box3();
fragIds.forEach(function(fragId) {
fragList.getWorldBounds(fragId, fragbBox);
nodebBox.union(fragbBox);
});
return resolve(nodebBox);
}
catch(ex){
return reject(ex);
}
});
}
这就是我从最小值、最大值创建框的方式:
let ddd = new THREE.Vector3(min.x, min.y, min.z);
let ddu = new THREE.Vector3(min.x, min.y, max.z);
let dud = new THREE.Vector3(min.x, max.y, min.z);
let udd = new THREE.Vector3(max.x, min.y, min.z);
let duu = new THREE.Vector3(min.x, max.y, max.z);
let uud = new THREE.Vector3(max.x, max.y, min.z);
let udu = new THREE.Vector3(max.x, min.y, max.z);
let uuu = new THREE.Vector3(max.x, max.y, max.z);
this.drawVertices([ddd,ddu,dud,udd,duu,uud,udu,uuu]);
let facesPoints = [
{
BL: ddd.clone(),
UL: ddu.clone(),
UR: udu.clone(),
BR: udd.clone()
},
{
BL: udd.clone(),
UL: udu.clone(),
UR: uuu.clone(),
BR: uud.clone()
},
{
BL: uud.clone(),
UL: uuu.clone(),
UR: duu.clone(),
BR: dud.clone()
},
{
BL: dud.clone(),
UL: duu.clone(),
UR: ddu.clone(),
BR: ddd.clone()
}
];
我想避免对所有顶点对的所有距离进行排序并取前两个的蛮力方法。
是否有另一种数据结构将公开一个立方体的 8 个点,而不是我可以给它多边形来构建它的 2 个点,就像在上面的函数中一样?
边界框与世界轴对齐。如果您的形状在 space 中旋转,只需将形状的世界矩阵应用于其边界框(的副本)。那应该给你形状的世界边界框。
在下面的示例中,红色立方体的边界框是根据局部 space 计算得出的,我将红色立方体的矩阵应用于边界框。绿色立方体的边界框在每一帧都重新计算,导致与世界轴对齐的框随着框的旋转而增大和缩小。
var renderer, scene, camera, controls, stats, rotationMatrix, tmpPos, cube1, cube2, cube1BBox, cube2BBox;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function populateExample(){
rotationMatrix = new THREE.Matrix4().makeRotationY(0.5 * (Math.PI / 180));
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
cube1Mat = new THREE.MeshPhongMaterial({ color: "red" }),
cube2Mat = new THREE.MeshPhongMaterial({ color: "green" });
cube1Mat.polygonOffset = true;
cube1Mat.polygonOffsetFactor = 1;
cube1Mat.polygonOffsetUnits = 0.5;
cube2Mat.polygonOffset = true;
cube2Mat.polygonOffsetFactor = 1;
cube2Mat.polygonOffsetUnits = 0.5;
cube1 = new THREE.Mesh(cubeGeo, cube1Mat);
scene.add(cube1);
cube2 = new THREE.Mesh(cubeGeo, cube2Mat);
scene.add(cube2);
cube1BBox = new THREE.BoxHelper(cube1, 0xffffff);
scene.add(cube1BBox);
cube2BBox = new THREE.BoxHelper(cube2, 0xffffff);
scene.add(cube2BBox);
cube1.position.set(-10, 0, 0);
cube2.position.set(10, 0, 0);
cube1BBox.position.set(-10, 0, 0);
}
function exampleRenderAction(){
tmpPos.copy(cube1.position);
cube1.position.sub(tmpPos);
cube1.updateMatrix();
cube1.applyMatrix(rotationMatrix);
cube1.position.add(tmpPos);
cube1.updateMatrix();
cube1BBox.matrix.copy(cube1.matrix);
tmpPos.copy(cube2.position);
cube2.position.sub(tmpPos);
cube2.updateMatrix();
cube2.applyMatrix(rotationMatrix);
cube2.position.add(tmpPos);
cube2.updateMatrix();
cube2BBox.update();
}
function init() {
tmpPos = new THREE.Vector3();
rotation = 0;
rotationSpeed = 0.5;
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.set(0, 40, 40);
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateExample();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
exampleRenderAction();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function () {
function addScript(url, callback) {
callback = callback || function () { };
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function () {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
threeReady();
})
})
})
})();
基于评论中的说明的扩展答案:
边界框是 THREE.Box3
,其中包含 min
和 max
THREE.Vector3
。因此,要像您已经做的那样获得边界框的 8 个角:
var corners = [
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z),
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.max.z),
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z),
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.min.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z)
];
你可以随意安排这些。要将这些转换为世界坐标,您需要再执行一个步骤。 请注意,以下步骤是破坏性的,因此如果您需要保留原始角值,则需要保存它们的副本。
顶点当前是对象的局部顶点,因此您需要使用对象的矩阵更新它们:
for(var i = 0, len = corners.length; i < len; ++0){
// this will apply all transformations from all parents
corners[i].applyMatrix4(myObj.matrixWorld);
}
或者,您可以使用 localToWorld
将点转换为世界坐标。
for(var i = 0, len = corners.length; i < len; ++0){
// this literally does the same thing as the code above
myObj.localToWorld(corners[i]);
}
但是不要两者都做,否则你最终会得到不正确的值。
我找到了一种方法,首先我使用 Autodesk 查看器扩展(网格数据)中的此功能将所有顶点收集到 :
static getMeshVertices(viewer, fragId) {
var fragProxy = viewer.impl.getFragmentProxy(
viewer.model,
fragId);
var renderProxy = viewer.impl.getRenderProxy(
viewer.model,
fragId);
fragProxy.updateAnimTransform();
var matrix = new THREE.Matrix4();
fragProxy.getWorldMatrix(matrix);
const verticesSet = new GeneralSet();
const geometry = renderProxy.geometry;
const attributes = geometry.attributes;
if (attributes && attributes.index !== undefined) {
const indices = attributes.index.array || geometry.ib;
const positions = geometry.vb ? geometry.vb : attributes.position.array;
const stride = geometry.vb ? geometry.vbstride : 3;
let offsets = geometry.offsets;
if (!offsets || offsets.length === 0) {
offsets = [{ start: 0, count: indices.length, index: 0 }];
}
for (var oi = 0, ol = offsets.length; oi < ol; ++oi) {
const start = offsets[oi].start;
const count = offsets[oi].count;
const index = offsets[oi].index;
for (var i = start, il = start + count; i < il; i += 3) {
const vA = new THREE.Vector3();
const vB = new THREE.Vector3();
const vC = new THREE.Vector3();
const a = index + indices[i];
const b = index + indices[i + 1];
const c = index + indices[i + 2];
vA.fromArray(positions, a * stride);
vB.fromArray(positions, b * stride);
vC.fromArray(positions, c * stride);
vA.applyMatrix4(matrix);
vB.applyMatrix4(matrix);
vC.applyMatrix4(matrix);
verticesSet.add(vA);
verticesSet.add(vB);
verticesSet.add(vC);
}
}
return verticesSet;
}
else {
var positions = geometry.vb ? geometry.vb : attributes.position.array;
var stride = geometry.vb ? geometry.vbstride : 3;
for (var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9) {
let vA = new THREE.Vector3();
let vB = new THREE.Vector3();
let vC = new THREE.Vector3();
var a = i;
var b = i + 1;
var c = i + 2;
vA.fromArray(positions, a * stride);
vB.fromArray(positions, b * stride);
vC.fromArray(positions, c * stride);
vA.applyMatrix4(matrix);
vB.applyMatrix4(matrix);
vC.applyMatrix4(matrix);
verticesSet.add(vA);
verticesSet.add(vB);
verticesSet.add(vC);
}
return verticesSet;
}
}
然后我使用了这里的图直径算法:https://cs.stackexchange.com/a/213/43035一次在顶点集上(好像该集代表一个完整的图)得到两个对角,我们称它们为 u,w。
然后我从顶点集中删除了 u、w 并再次 运行 图形直径,得到另外两个角。
现在有了四个角,就可以生成所有其余的角,并对它们进行排序,有 3 个条件来检查 4 个角中的每一个(这将显示其他 4 个角)、与相机的距离(更近或更近)进一步),高度(上角或下角)和对象中间的左侧或右侧(使用十字和点,像这样https://forum.unity3d.com/threads/left-right-test-function.31420/(他们有js代码))。
这会给你 8 个角,这样你就会知道哪个角在哪里,角在对象上,无论对象如何与轴对齐都没有关系。
我正在尝试创建对象的精确边界框,但似乎如果对象未与轴对齐(我认为),则框未与对象对齐。
例如:
粉红色和靠近橙色的顶点是这面墙的 Box3.min、Box3.max,但是你看那面墙上没有红色、绿色和蓝色。您可以忽略水绿色顶点。
这是创建边界框(returns Box3)的代码:
static getWorldBoundingBox(model, dbId) {
return new Promise(async(resolve, reject)=>{
try{
var fragIds = await ViewerToolkit.getFragIds(
model, dbId);
if(!fragIds.length){
return reject('No geometry, invalid dbId?');
}
var fragList = model.getFragmentList();
var fragbBox = new THREE.Box3();
var nodebBox = new THREE.Box3();
fragIds.forEach(function(fragId) {
fragList.getWorldBounds(fragId, fragbBox);
nodebBox.union(fragbBox);
});
return resolve(nodebBox);
}
catch(ex){
return reject(ex);
}
});
}
这就是我从最小值、最大值创建框的方式:
let ddd = new THREE.Vector3(min.x, min.y, min.z);
let ddu = new THREE.Vector3(min.x, min.y, max.z);
let dud = new THREE.Vector3(min.x, max.y, min.z);
let udd = new THREE.Vector3(max.x, min.y, min.z);
let duu = new THREE.Vector3(min.x, max.y, max.z);
let uud = new THREE.Vector3(max.x, max.y, min.z);
let udu = new THREE.Vector3(max.x, min.y, max.z);
let uuu = new THREE.Vector3(max.x, max.y, max.z);
this.drawVertices([ddd,ddu,dud,udd,duu,uud,udu,uuu]);
let facesPoints = [
{
BL: ddd.clone(),
UL: ddu.clone(),
UR: udu.clone(),
BR: udd.clone()
},
{
BL: udd.clone(),
UL: udu.clone(),
UR: uuu.clone(),
BR: uud.clone()
},
{
BL: uud.clone(),
UL: uuu.clone(),
UR: duu.clone(),
BR: dud.clone()
},
{
BL: dud.clone(),
UL: duu.clone(),
UR: ddu.clone(),
BR: ddd.clone()
}
];
我想避免对所有顶点对的所有距离进行排序并取前两个的蛮力方法。
是否有另一种数据结构将公开一个立方体的 8 个点,而不是我可以给它多边形来构建它的 2 个点,就像在上面的函数中一样?
边界框与世界轴对齐。如果您的形状在 space 中旋转,只需将形状的世界矩阵应用于其边界框(的副本)。那应该给你形状的世界边界框。
在下面的示例中,红色立方体的边界框是根据局部 space 计算得出的,我将红色立方体的矩阵应用于边界框。绿色立方体的边界框在每一帧都重新计算,导致与世界轴对齐的框随着框的旋转而增大和缩小。
var renderer, scene, camera, controls, stats, rotationMatrix, tmpPos, cube1, cube2, cube1BBox, cube2BBox;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function populateExample(){
rotationMatrix = new THREE.Matrix4().makeRotationY(0.5 * (Math.PI / 180));
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
cube1Mat = new THREE.MeshPhongMaterial({ color: "red" }),
cube2Mat = new THREE.MeshPhongMaterial({ color: "green" });
cube1Mat.polygonOffset = true;
cube1Mat.polygonOffsetFactor = 1;
cube1Mat.polygonOffsetUnits = 0.5;
cube2Mat.polygonOffset = true;
cube2Mat.polygonOffsetFactor = 1;
cube2Mat.polygonOffsetUnits = 0.5;
cube1 = new THREE.Mesh(cubeGeo, cube1Mat);
scene.add(cube1);
cube2 = new THREE.Mesh(cubeGeo, cube2Mat);
scene.add(cube2);
cube1BBox = new THREE.BoxHelper(cube1, 0xffffff);
scene.add(cube1BBox);
cube2BBox = new THREE.BoxHelper(cube2, 0xffffff);
scene.add(cube2BBox);
cube1.position.set(-10, 0, 0);
cube2.position.set(10, 0, 0);
cube1BBox.position.set(-10, 0, 0);
}
function exampleRenderAction(){
tmpPos.copy(cube1.position);
cube1.position.sub(tmpPos);
cube1.updateMatrix();
cube1.applyMatrix(rotationMatrix);
cube1.position.add(tmpPos);
cube1.updateMatrix();
cube1BBox.matrix.copy(cube1.matrix);
tmpPos.copy(cube2.position);
cube2.position.sub(tmpPos);
cube2.updateMatrix();
cube2.applyMatrix(rotationMatrix);
cube2.position.add(tmpPos);
cube2.updateMatrix();
cube2BBox.update();
}
function init() {
tmpPos = new THREE.Vector3();
rotation = 0;
rotationSpeed = 0.5;
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.set(0, 40, 40);
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateExample();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
exampleRenderAction();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function () {
function addScript(url, callback) {
callback = callback || function () { };
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function () {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
threeReady();
})
})
})
})();
基于评论中的说明的扩展答案:
边界框是 THREE.Box3
,其中包含 min
和 max
THREE.Vector3
。因此,要像您已经做的那样获得边界框的 8 个角:
var corners = [
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z),
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.max.z),
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z),
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.min.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z)
];
你可以随意安排这些。要将这些转换为世界坐标,您需要再执行一个步骤。 请注意,以下步骤是破坏性的,因此如果您需要保留原始角值,则需要保存它们的副本。
顶点当前是对象的局部顶点,因此您需要使用对象的矩阵更新它们:
for(var i = 0, len = corners.length; i < len; ++0){
// this will apply all transformations from all parents
corners[i].applyMatrix4(myObj.matrixWorld);
}
或者,您可以使用 localToWorld
将点转换为世界坐标。
for(var i = 0, len = corners.length; i < len; ++0){
// this literally does the same thing as the code above
myObj.localToWorld(corners[i]);
}
但是不要两者都做,否则你最终会得到不正确的值。
我找到了一种方法,首先我使用 Autodesk 查看器扩展(网格数据)中的此功能将所有顶点收集到
static getMeshVertices(viewer, fragId) {
var fragProxy = viewer.impl.getFragmentProxy(
viewer.model,
fragId);
var renderProxy = viewer.impl.getRenderProxy(
viewer.model,
fragId);
fragProxy.updateAnimTransform();
var matrix = new THREE.Matrix4();
fragProxy.getWorldMatrix(matrix);
const verticesSet = new GeneralSet();
const geometry = renderProxy.geometry;
const attributes = geometry.attributes;
if (attributes && attributes.index !== undefined) {
const indices = attributes.index.array || geometry.ib;
const positions = geometry.vb ? geometry.vb : attributes.position.array;
const stride = geometry.vb ? geometry.vbstride : 3;
let offsets = geometry.offsets;
if (!offsets || offsets.length === 0) {
offsets = [{ start: 0, count: indices.length, index: 0 }];
}
for (var oi = 0, ol = offsets.length; oi < ol; ++oi) {
const start = offsets[oi].start;
const count = offsets[oi].count;
const index = offsets[oi].index;
for (var i = start, il = start + count; i < il; i += 3) {
const vA = new THREE.Vector3();
const vB = new THREE.Vector3();
const vC = new THREE.Vector3();
const a = index + indices[i];
const b = index + indices[i + 1];
const c = index + indices[i + 2];
vA.fromArray(positions, a * stride);
vB.fromArray(positions, b * stride);
vC.fromArray(positions, c * stride);
vA.applyMatrix4(matrix);
vB.applyMatrix4(matrix);
vC.applyMatrix4(matrix);
verticesSet.add(vA);
verticesSet.add(vB);
verticesSet.add(vC);
}
}
return verticesSet;
}
else {
var positions = geometry.vb ? geometry.vb : attributes.position.array;
var stride = geometry.vb ? geometry.vbstride : 3;
for (var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9) {
let vA = new THREE.Vector3();
let vB = new THREE.Vector3();
let vC = new THREE.Vector3();
var a = i;
var b = i + 1;
var c = i + 2;
vA.fromArray(positions, a * stride);
vB.fromArray(positions, b * stride);
vC.fromArray(positions, c * stride);
vA.applyMatrix4(matrix);
vB.applyMatrix4(matrix);
vC.applyMatrix4(matrix);
verticesSet.add(vA);
verticesSet.add(vB);
verticesSet.add(vC);
}
return verticesSet;
}
}
然后我使用了这里的图直径算法:https://cs.stackexchange.com/a/213/43035一次在顶点集上(好像该集代表一个完整的图)得到两个对角,我们称它们为 u,w。
然后我从顶点集中删除了 u、w 并再次 运行 图形直径,得到另外两个角。
现在有了四个角,就可以生成所有其余的角,并对它们进行排序,有 3 个条件来检查 4 个角中的每一个(这将显示其他 4 个角)、与相机的距离(更近或更近)进一步),高度(上角或下角)和对象中间的左侧或右侧(使用十字和点,像这样https://forum.unity3d.com/threads/left-right-test-function.31420/(他们有js代码))。
这会给你 8 个角,这样你就会知道哪个角在哪里,角在对象上,无论对象如何与轴对齐都没有关系。