![](/img/trans.png)
[英]How do I make a 3D bounding box on a SkinnedMesh object in Unity?
[英]Finding an oriented bounding box for a set of 3D points in Unity
我有一組 3D 點,或者實際上是小球體,我需要使用 Unity 3D 用盡可能小的 3D 框將其包圍。
在封閉框只能移動和縮放的情況下,解決方案非常簡單,您只需遍歷所有點並封裝每個點。 但我還需要為盒子找到最佳的方向。
因此,為了用 ASCII 說明問題,給定一個只有兩點的基本 2D 場景:
Y
| * (0,1)
|
|
|
| * (1,0)
-------------------- X
使用常規增長的邊界框,您最終會得到一個包含大部分空白空間的非常大的封閉框,而在這種情況下,我需要一個非常薄且圍繞 Z 軸旋轉約 45 度的框。 基本上只是一條連接兩點的線。
我永遠不知道有多少點需要組合在一起。 如前所述,它必須在 3D 中工作。
到目前為止,我只嘗試了基本方法,即不旋轉封裝盒以獲得最佳配合。 結果真的離我需要的很遠。
我正在考慮一種基於遺傳算法的蠻力方法,在該方法中我生成大量隨機框,並簡單地 select 面積最小但仍包含所有點的那個。 但是,這太慢了。
GameObject go = points[0];
Bounds b = new Bounds(go.transform.position,go.transform.localScale);
for (int i=1;i<points.Count;i++)
{
go = points[i];
b.Encapsulate(new Bounds(go.transform.position, go.transform.localScale));
}
GameObject containingBox = Instantiate(boxPrefab);
containingBox.transform.position = b.center;
containingBox.transform.localScale = b.size;
containingBox.transform.rotation= Quaternion.Identity; //How to calculate?
嘿,我搜索了一下,我發現了一個非常強大的庫,它或多或少地提供了你正在尋找的東西,甚至積極支持 Unity 類型:
在 Unity 項目中的實現非常簡單
.zip
geometry3Sharp-master
文件夾解壓到你的Assets
文件夾中在 Unity 下ProjectSettings
→ Player
→ Other Settings
→ Configuration
→ Scripting Define Symbols
insert
G3_USING_UNITY;
正如自述文件中所解釋的那樣:
geometry3Sharp
支持 Unity 類型的透明轉換。 要啟用此功能,請在 Unity 項目中定義G3_USING_UNITY
,方法是將此字符串添加到 Player Settings 中的 Scripting Define Symbols 框中。
然后,您可以簡單地計算給定Vector3
點數組的定向邊界框的邊緣,如下所示:
using UnityEngine;
using g3;
public class Example : MonoBehaviour
{
// Just for the demo I used Transforms so I can simply move them around in the scene
public Transform[] transforms;
private void OnDrawGizmos()
{
// First wehave to convert the Unity Vector3 array
// into the g3 type g3.Vector3d
var points3d = new Vector3d[transforms.Length];
for (var i = 0; i < transforms.Length; i++)
{
// Thanks to the g3 library implictely casted from UnityEngine.Vector3 to g3.Vector3d
points3d[i] = transforms[i].position;
}
// BOOM MAGIC!!!
var orientedBoundingBox = new ContOrientedBox3(points3d);
// Now just convert the information back to Unity Vector3 positions and axis
// Since g3.Vector3d uses doubles but Unity Vector3 uses floats
// we have to explicitly cast to Vector3
var center = (Vector3)orientedBoundingBox.Box.Center;
var axisX = (Vector3)orientedBoundingBox.Box.AxisX;
var axisY = (Vector3)orientedBoundingBox.Box.AxisY;
var axisZ = (Vector3)orientedBoundingBox.Box.AxisZ;
var extends = (Vector3)orientedBoundingBox.Box.Extent;
// Now we can simply calculate our 8 vertices of the bounding box
var A = center - extends.z * axisZ - extends.x * axisX - axisY * extends.y;
var B = center - extends.z * axisZ + extends.x * axisX - axisY * extends.y;
var C = center - extends.z * axisZ + extends.x * axisX + axisY * extends.y;
var D = center - extends.z * axisZ - extends.x * axisX + axisY * extends.y;
var E = center + extends.z * axisZ - extends.x * axisX - axisY * extends.y;
var F = center + extends.z * axisZ + extends.x * axisX - axisY * extends.y;
var G = center + extends.z * axisZ + extends.x * axisX + axisY * extends.y;
var H = center + extends.z * axisZ - extends.x * axisX + axisY * extends.y;
// And finally visualize it
Gizmos.DrawLine(A, B);
Gizmos.DrawLine(B, C);
Gizmos.DrawLine(C, D);
Gizmos.DrawLine(D, A);
Gizmos.DrawLine(E, F);
Gizmos.DrawLine(F, G);
Gizmos.DrawLine(G, H);
Gizmos.DrawLine(H, E);
Gizmos.DrawLine(A, E);
Gizmos.DrawLine(B, F);
Gizmos.DrawLine(D, H);
Gizmos.DrawLine(C, G);
// And Here we ca just be amazed ;)
}
}
當然還有像
orientedBoundingBox.Box.Contains(Vector3d)
用於確定給定點是否位於該框內。
只是因為 Ruzihm 問道:
當然,您可以稍微更改上面的腳本以簡單地使用實際的網格頂點:
public MeshFilter[] meshFilters;
private void OnDrawGizmos()
{
var vertices = new List<Vector3>();
foreach (var meshFilter in meshFilters)
{
// have to multiply the vertices' positions
// with the lossyScale and add it to the transform.position
vertices.AddRange(meshFilter.sharedMesh.vertices.Select(vertex => meshFilter.transform.position + Vector3.Scale(vertex, meshFilter.transform.lossyScale)));
}
var points3d = new Vector3d[vertices.Count];
for (var i = 0; i < vertices.Count; i++)
{
points3d[i] = vertices[i];
}
// ...
// From here the code is the same as above
看起來基本相同
不要想到盒子然后嘗試適應它。 在這種情況下更容易生成框。 所以先點,后框。
我在這里為 2D 進行了說明,但與 3D 的唯一區別是第三個角度、不同的角度偏移以及要處理的更多頂點/面。
正如評論中所指出的,這可能不是一個完美的解決方案。 如果一個或兩個末端具有多個點,這些點與相對末端接近或距離相等,但橫向分開,此方法將為您提供最小邊界框的適當近似值,但不會給出最小邊界盒子可能。
Ruzihm的插圖:
但是,他的插圖有點錯誤,因為我的解決方案不是軸對齊的。 這是它將在右側以紅色或橙色實現的效果,具體取決於選擇哪個點作為頂點:
在尋找可以提供數學上完美的最小邊界框的解決方案時,我發現了另一個 (C++) 問題及其答案。 現在我沒有時間通讀它並在這里為 Unity/C# 復制解決方案,所以我只會指出它。 也許我或其他貢獻者可以稍后編輯該副本。
我能夠通過將 OpenCV 用於 Unity 來解決這個問題。 我使用了 minAreaRect 方法,它計算 2D 中點周圍的擬合邊界框(我首先將它們投影到 X/Z 平面上)。
此方法方便地返回一個矩形,具有中心點、角度和寬度/高度。 從這里開始,很容易將其擴展到 3D 盒子。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.