如何找到两个轴对齐立方体之间相交的体积和顶点?

Enr*_*era 4 c# unity-game-engine

如何找到与轴对齐的两个立方体相交的体积?立方体可以具有不同的尺寸和位置。(我添加一张图片来显示两个立方体的简单示例)

经过深入研究,我非常确定Unity中不存在用于此目的的特定函数,解决这个问题的唯一方法是数理逻辑。例如,我的第一个想法是:

  1. 找到立方体“交点”的 8 个顶点(图中的 B)。

  2. 尝试用这个顶点构建一个新的立方体。

  3. 求立方体“交点”的大小和体积。

Unity 允许查找:

  • 每个主立方体的中心(图中的 A)带有“Bounds.centre”;
  • 每个主立方体(图像中的 A)从立方体中心(边界大小的一半)到“Bounds.extents”的范围。

此处提供文档: https: //docs.unity3d.com/ScriptReference/Bounds.html

但是,我找不到一种方法来获得每个立方体的顶点。其次,一个逻辑函数可以找到所找到的 16 个顶点中的 8 个是用于构建立方体“交点”的正确顶点。

你有什么帮助或建议吗?

图像: 在此输入图像描述

der*_*ugo 6

如果正如你所说,立方体将始终与 Unity 世界轴对齐(=> 边界 = 对撞机/渲染器体积),你可能可以简单地执行类似的操作

  • Bounds两个盒子的
  • 取这些的minmax
  • => 使用最小值的最大值和最大值的最小值分别检查每个轴上的重叠程度

你得到的是重叠框的新的最小和最大点。

这是足够的信息

  • 通过将这些最小值和最大值之间的向量的各个分量相乘来计算体积。

  • 通过获取每个轴的最小值和最大值之间的所有排列来获取所有顶点。

就像是

public class OverlapArea
{
    public readonly Vector3 min;
    public readonly Vector3 max;

    public readonly float volume;
    public Vector3 frontBottomLeft => min;
    public readonly Vector3 frontBottomRight;
    public readonly Vector3 frontTopLeft;
    public readonly Vector3 frontTopRight;
    public readonly Vector3 backBottomLeft;
    public readonly Vector3 backBottomRight;
    public readonly Vector3 backTopLeft;
    public Vector3 backTopRight => max;

    public readonly Bounds bounds;

    public OverlapArea(Bounds a, Bounds b)
    {
        // The min and max points
        var minA = a.min;
        var maxA = a.max;
        var minB = b.min;
        var maxB = b.max;

        min.x = Mathf.Max(minA.x, minB.x);
        min.y = Mathf.Max(minA.y, minB.y);
        min.z = Mathf.Max(minA.z, minB.z);

        max.x = Mathf.Min(maxA.x, maxB.x);
        max.y = Mathf.Min(maxA.y, maxB.y);
        max.z = Mathf.Min(maxA.z, maxB.z);

        frontBottomRight = new Vector3(max.x, min.y, min.z);
        frontTopLeft = new Vector3(min.x, max.y, min.z);
        frontTopRight = new Vector3(max.x, max.y, min.z);
        backBottomLeft = new Vector3(min.x, min.y, max.z);
        backBottomRight = new Vector3(max.x, min.y, max.z);
        backTopLeft = new Vector3(min.x, max.y, max.z);

        // The diagonal of this overlap box itself
        var diagonal = max - min;
        volume = diagonal.x * diagonal.y * diagonal.z;

        bounds.SetMinMax(min, max);
    }

    public static bool GetOverlapArea(Bounds a, Bounds b, out OverlapArea overlapArea)
    {
        overlapArea = default;

        // If they are not intersecting we can stop right away ;)
        if (!a.Intersects(b)) return false;

        overlapArea = new OverlapArea(a, b);

        return true;
    }
}
    
    
Run Code Online (Sandbox Code Playgroud)

因此,为了获得重叠信息,您可以这样做,例如

// I intentionally used the Bounds as parameters for the method because you can
// either use the Renderer bounds
var boundsA = cubeA.GetComponent<Renderer>().bounds;
// or use Collider bounds in your case they should be equal
var boundsB = cubeB.GetComponent<Collider>().bounds;

if(GetOverlapArea(boundsA, boundsB, out var overlap))
{
    // Now in overlap you have all the information you wanted
}
Run Code Online (Sandbox Code Playgroud)

现在为了真正获得重叠网格(如果那是你要去的地方)你有两个选择

使用给定的边缘点并自己实际创建一个网格(注意顶点位于世界空间中,因此对象本身或任何父对象上不应缩放)

...

var mesh = new Mesh
{
    vertices = new[]
    {
        overlapArea.frontBottomLeft,
        overlapArea.frontBottomRight,
        overlapArea.frontTopLeft,
        overlapArea.frontTopRight,

        overlapArea.backBottomLeft,
        overlapArea.backBottomRight,
        overlapArea.backTopLeft,
        overlapArea.backTopRight
    },
    triangles = new[]
    {
        // Front
        0, 2, 1,
        1, 2, 3,
        // Back
        5, 7, 4,
        4, 7, 6,
        // Left
        4, 6, 2,
        4, 2, 0,
        // Right
        1, 7, 5,
        1, 3, 7,
        // Top
        2, 7, 3,
        2, 6, 7,
        // Bottom
        0, 4, 1,
        1, 4, 5
    }
}
Run Code Online (Sandbox Code Playgroud)

作为一个小演示

public class Example : MonoBehaviour
{
    public Renderer CubeA;
    public Renderer CubeB;

    public Material overlapMaterial;

    private MeshFilter overlap;
    private readonly Vector3[] overlapVertices = new Vector3[8];

    public void Awake()
    {
        overlap = new GameObject("Overlap", typeof(MeshRenderer)).AddComponent<MeshFilter>();

        var overlapMesh = new Mesh
        {
            vertices = overlapVertices,
            triangles = new[]
            {
                // Front
                0, 2, 1,
                1, 2, 3,

                // Back
                5, 7, 4,
                4, 7, 6,

                // Left
                4, 6, 2,
                4, 2, 0,

                // Right
                1, 7, 5,
                1, 3, 7,

                // Top
                2, 7, 3,
                2, 6, 7,

                // Bottom
                0, 4, 1,
                1, 4, 5
            }
        };

        overlap.mesh = overlapMesh;
        overlap.GetComponent<Renderer>().material = overlapMaterial;
    }
    
    public void Update()
    {
        if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
        {
            overlap.gameObject.SetActive(true);

            overlap.mesh.vertices = new[]
            {
                overlapArea.frontBottomLeft,
                overlapArea.frontBottomRight,
                overlapArea.frontTopLeft,
                overlapArea.frontTopRight,

                overlapArea.backBottomLeft,
                overlapArea.backBottomRight,
                overlapArea.backTopLeft,
                overlapArea.backTopRight
            };

            overlap.mesh.RecalculateBounds();
        }
        else
        {
            overlap.gameObject.SetActive(false);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

或者您可以使用已经存在的原始立方体(默认的 Unity 立方体)并将其设置为正确的坐标并像这样缩放它

overlapVisualizer.transform.position = overlap.bounds.center;
// note we set the local scale so there should be no parent scaling
overlapVisualizer.transform.localScale = overlap.bounds.size;
Run Code Online (Sandbox Code Playgroud)

再看一个小演示

public class Example : MonoBehaviour
{
    public Renderer CubeA;
    public Renderer CubeB;

    public Transform overlap;

    public void Update()
    {
        if (OverlapArea.GetOverlapArea(CubeA.bounds, CubeB.bounds, out var overlapArea))
        {
            overlap.gameObject.SetActive(true);

            overlap.position = overlapArea.bounds.center;
            overlap.localScale = overlapArea.bounds.size;
        }
        else
        {
            overlap.gameObject.SetActive(false);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述