# 水面渲染-浮力的一种实现

## 体积的计算

#### 物体总体积的计算

/// <summary>
/// 计算体积
/// </summary>
private void CalualateVolume()
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = mf.mesh;
float volume = 0f;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
for (int i = 0; i < mesh.triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
Vector3 a = p1 - p2;
Vector3 b = p1 - p3;
Vector3 c = p1 - Vector3.zero;

volume += (Vector3.Dot(a, Vector3.Cross(b, c))) / 6f;

}

m_Volume = Mathf.Abs(volume) * transform.localScale.x * transform.localScale.y * transform.localScale.z;
}

#### 计算物体浸入水中部分的体积

dbrizov/Unity-WaterBuoyancy项目提出体素这个概念，它把一个物体量化成均匀分布的点，只需要计算浸入水中的点的数目，便可近似得到物体浸入水中部分的体积

##### 获取体素列表
    private void CalualateVoxels()
{
Quaternion initialRotation = this.transform.rotation;
this.transform.rotation = Quaternion.identity;
Bounds bounds = m_Bounds;
this.voxelSize.x = bounds.size.x / VoxelSize;
this.voxelSize.y = bounds.size.y / VoxelSize;
this.voxelSize.z = bounds.size.z / VoxelSize;
List<Vector3> voxels = new List<Vector3>( VoxelSize * VoxelSize * VoxelSize);

for (int j = 0; j < VoxelSize; j++)
{
for (int i = 0; i < VoxelSize; i++)
{
for (int k = 0; k < VoxelSize; k++)
{
float pX = bounds.min.x + this.voxelSize.x * (0.5f + i);
float pY = bounds.min.y + this.voxelSize.y * (0.5f + j);
float pZ = bounds.min.z + this.voxelSize.z * (0.5f + k);

Vector3 point = new Vector3(pX, pY, pZ);
if (IsPointInsideCollider(point))
{
}
}
}
}

transform.rotation = initialRotation;

m_Voxels = voxels.ToArray();
}

private bool IsPointInsideCollider(Vector3 point)
{
float rayLength = m_Bounds.size.magnitude;
Ray ray = new Ray(point, m_Collider.transform.position - point);
RaycastHit hit;

if (Physics.Raycast(ray, out hit, rayLength))
{
if (hit.collider == m_Collider)
{
return false;
}
}

return true;
}

## 浮力实现1.0版

#### 总浮力的计算

int len = m_Voxels.Length;
float submergedVolume = 0f;
Vector3 force = water.Density * m_Volume * -Physics.gravity / m_Voxels.Length;//单个体素受到的浮力
for (int i = 0; i < len; i++)
{
Vector3 worldPoint = transform.TransformPoint(m_Voxels[i]);

float submergedFactor = 0;

if (worldPoint.y < water.transform.position.y)
{
submergedVolume += 1;
}

}
m_Rigidbody.AddForce(force * submergedVolume);

## 浮力实现2.0版

dbrizov/Unity-WaterBuoyancy是这样实现的

Vector3 worldPoint = transform.TransformPoint(m_Voxels[i]);

float submergedFactor = 0;

if (worldPoint.y < water.transform.position.y)
{
submergedFactor = 1;
submergedVolume += submergedFactor;
}

Vector3 surfaceNormal = water.GetSurfaceNormal(worldPoint);
Quaternion surfaceRotation = Quaternion.FromToRotation(water.transform.up, surfaceNormal);
surfaceRotation = Quaternion.Slerp(surfaceRotation, Quaternion.identity, submergedFactor);

Vector3 finalVoxelForce = surfaceRotation * force * submergedFactor;
Debug.DrawLine(worldPoint, worldPoint + finalVoxelForce.normalized, Color.blue);