基本思想
Unity中计算两个物体相交部分的体积与计算任意形状模型体积的方法类似,具体思想见:Unity 计算任意形状模型物体的体积。我们首先计算出两个物体各自的体积,再计算出两个物体组成的体积之和,相减即为相交部分的体积。
实现方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class IntersectVolume : MonoBehaviour
{
private int sampleCount = 100;//每个方向选取100个点,包围盒内一共选取了100万个点
private void Start()
{
float volume = CalculateIntersectVolume();
}
//检测点是否在模型内部
bool IsInCollider(MeshCollider other, Vector3 center, Vector3 point)
{
Vector3 direction = center - point;
RaycastHit[] hits = Physics.RaycastAll(point, direction);
foreach (RaycastHit hit in hits)
{
if (hit.collider == other)
{
// we hit it so we were outside it
return false;
}
}
// no hits probably means we're inside it
return true;
}
public float CalculateIntersectVolume()
{
GameObject object1 = GameObject.Find("object1");//更改为场景中要计算体积的物体的名称
GameObject object2 = GameObject.Find("object2");
MeshCollider object1_mc = object1.GetComponent<MeshCollider>();
MeshCollider object2_mc = object2.GetComponent<MeshCollider>();
Vector3 object1_center = object1_mc.bounds.center;
Vector3 object2_center = object2_mc.bounds.center;
Matrix4x4 localToWorld_object1 = object1.transform.localToWorldMatrix;
Matrix4x4 localToWorld_object2 = object2.transform.localToWorldMatrix;
Vector3[] vertices_object1 = object1.GetComponent<MeshFilter>().mesh.vertices;
Vector3[] vertices_object2 = object2.GetComponent<MeshFilter>().mesh.vertices;
float[] x = new float[vertices_object2.Length + vertices_object1.Length];
float[] y = new float[vertices_object2.Length + vertices_object1.Length];
float[] z = new float[vertices_object2.Length + vertices_object1.Length];
//将局部坐标转换为世界坐标,并传递给数组
for (int i = 0; i < vertices_object2.Length; i++)
{
Vector3 world_v = localToWorld_object2.MultiplyPoint3x4(vertices_object2[i]);
x[i] = world_v.x;
y[i] = world_v.y;
z[i] = world_v.z;
}
for (int i = vertices_object2.Length; i < vertices_object2.Length + vertices_object1.Length; i++)
{
Vector3 world_v = localToWorld_object1.MultiplyPoint3x4(vertices_object1[i - vertices_object2.Length]);
x[i] = world_v.x;
y[i] = world_v.y;
z[i] = world_v.z;
}
//从小到大排序
Array.Sort(x);
Array.Sort(y);
Array.Sort(z);
//包围盒三边长度
float x_length = x[x.Length - 1] - x[0];
float y_length = y[y.Length - 1] - y[0];
float z_length = z[z.Length - 1] - z[0];
//选点步长
float lerp_x = x_length / sampleCount;
float lerp_y = y_length / sampleCount;
float lerp_z = z_length / sampleCount;
int unionInside = 0, object1Inside = 0, object2Inside = 0 ;
bool inObject2;//检测点是否在object2内
bool inObject1;//检测点是否在object1内
for (int i = 0; i < sampleCount; i++)
{
for (int j = 0; j < sampleCount; j++)
{
for (int k = 0; k < sampleCount; k++)
{
inObject2 = false;
inObject1 = false;
Vector3 sampleDot = new Vector3(x[0] + i * lerp_x, y[0] + j * lerp_y, z[0] + k * lerp_z);
if (IsInCollider(object2_mc, object2_center, sampleDot))
{
inObject2 = true;
object2Inside++;
}
if (IsInCollider(object1_mc, object1_center, sampleDot))
{
inObject1 = true;
object1Inside++;
}
if (inObject2 || inObject1)
unionInside++;
}
}
}
//计算模型体积,单位为立方米
float intersectVolume = (float)(object1Inside + object2Inside - unionInside) / (sampleCount * sampleCount * sampleCount) * (x_length * y_length * z_length);
Debug.Log(intersectVolume);
return intersectVolume;
}
private void Update()
{
}
}
示例演示
在场景中添加两个cube,命名为object1和object2,坐标分别为(0,0,0)和(0.5,0.5,0.5),边长均为1,计算得出物体体积为0.126立方米,误差为0.8%,误差与体素化的精度有关。