前面章节中,我们已经将虚拟物体放置到ARCore检测到的平面上了,一切显得都是那么的神奇和谐,但ARCore之路可能比看起来要更加复杂一些。
在前面的章节中,我们制作的VisualDetectedPlane时,这个Prefab是有Mesh Collider的,理论上,这个平面是可以与其他碰撞体发生碰撞的。下面我们将Fox这个Prefab下挂载一个Rigidbody和Mesh Collider,然后我们设置一下Rigidbody下的Mass为1,钩上Use Gravity,即设置其质量为1,使用重力。如下图所示,编译运行一下。

按照我们设置与想法,实例化后的狐狸应该会与检测到的平面进行碰撞,然后可以稳稳的站在平面上,但事实是,这个狐狸会掉下去,也就是说我们想要的碰撞并没有发生。通过其他设置(不同的碰撞器、质量等),测试发现皆不能满足需求,希望ARCore以后会内建这个支持,但目前我们只能自己来处理。
一、思路
在AR场景中,如果虚拟物体不能与检测到的平面进行碰撞将会严重影响到用户体验,既然ARCore目前不能给我们提供一个便捷的方式来解决这个问题,我们就自己动手来解决它。解决的思路如下:
1、获取到ARCore检测到的所的平面。
2、对每一个平面进行处理,获取到该平面的最边沿的点。
3、对获取到的点这个平面进行网格化,也就是三角化。
4、对网格附加材质与Mesh Collider。
二、三角化
在计算机中我们用网格(Mesh)来描述表面,所有的网格又全部都由三角形组成,网格由图形硬件(GPU Graphics Processing Unit图形处理单元)进行处理来达到渲染显示的目的,目前,所有的GPU都能只能处理点、直线、三角形,也就是任何表面要想能被GPU处理则必须要将其使用先转化为点、直线、三角形组成的网格才行。使用三角形来构建网格,是因为三角形是平坦的并且拥有直边,所以他们可以完美地被用来显示平坦的和连续的表面,而且数据结构上简单便于处理,三角形可以构建任何表面,曲面或者是圆面也可以由大量小的三角形来近似组成。如果三角面数足够多,曲面或圆面可以非常平滑。

将一个不规则平面进行三角化是一个比较麻烦的过程,也超出了本文的范畴,我们只需要简单的作一介绍。对于给定点集,我们需要对点集进行处理,去掉空间位置太近的点、需要考虑点连成三角形的顺序(顺时针)、还要考虑中间空间的划分等等。三角化这个过程就是对给定点集进行处理以得到一个由三角形组成的网格以便进行下一步的处理。
三、实现方法
1、获取到ARCore检测到的所的平面。
这一步比较简单,我们只需要从Session中取出所有检测到的平面即可。
Session.GetTrackables(m_newPlanes, TrackableQueryFilter.New);
2、对每一个平面进行处理,获取到该平面的最边沿的点。
foreach (var plane in m_newPlanes)
{
var surfaceObj = new GameObject("ARSurface");
var arSurface = surfaceObj.AddComponent<ARSurface>();
arSurface.SetTrackedPlane(plane, m_surfaceMaterial);
}
ARCore DetectedPlane提供了一个GetBoundaryPolygon(List< Vector3 > boundaryPolygonPoints)方法,利用这个方法可以获取在Unity世界空间中表示平面的边界多边形的点列表(顺时针顺序)。
3、对获取到的点这个平面进行网格化,也就是三角化。
对点进行三角化我们直接写成一个类供调用,这里不详述。
4、对网格附加材质与Mesh Collider。
为了让我们的网格可视化,我们还需要对其赋材质,最主要的是要添加上Mesh Collider碰撞体,这样才能托住我们的物体让其不往下掉。
详细代码下如:
using GoogleARCore;
using System.Collections.Generic;
using UnityEngine;
public class ARSurface : MonoBehaviour
{
TrackedPlane m_trackedPlane;
MeshCollider m_meshCollider;
MeshFilter m_meshFilter;
MeshRenderer m_meshRenderer;
List<Vector3> m_points = new List<Vector3>();
List<Vector3> m_previousFramePoints = new List<Vector3>();
Mesh m_mesh;
void Awake()
{
m_meshCollider = gameObject.AddComponent<MeshCollider>();
m_meshFilter = gameObject.AddComponent<MeshFilter>();
m_meshRenderer = gameObject.AddComponent<MeshRenderer>();
m_mesh = new Mesh();
m_meshFilter.mesh = m_mesh;
m_meshCollider.sharedMesh = m_mesh;
Vector3 oneCentimeterUp = Vector3.up * 0.01f;
transform.Translate(oneCentimeterUp, Space.Self);
}
public void SetTrackedPlane(TrackedPlane plane, Material material)
{
m_trackedPlane = plane;
m_meshRenderer.material = material;
Update();
}
void Update()
{
if (m_trackedPlane == null)
{
return;
}
else if (m_trackedPlane.SubsumedBy != null)
{
Destroy(gameObject);
return;
}
else if (Session.Status != SessionStatus.Tracking)
{
m_meshRenderer.enabled = false;
m_meshCollider.enabled = false;
return;
}
m_meshRenderer.enabled = true;
m_meshCollider.enabled = true;
UpdateMeshIfNeeded();
}
void UpdateMeshIfNeeded()
{
m_trackedPlane.GetBoundaryPolygon(m_points);
if (AreVertexListsEqual(m_previousFramePoints, m_points))
{
return;
}
int[] indices = TriangulatorXZ.Triangulate(m_points); //三角化
m_mesh.Clear();
m_mesh.SetVertices(m_points);
m_mesh.SetIndices(indices, MeshTopology.Triangles, 0);
m_mesh.RecalculateBounds();
m_meshCollider.sharedMesh = null;
m_meshCollider.sharedMesh = m_mesh;
}
bool AreVertexListsEqual(List<Vector3> firstList, List<Vector3> secondList)
{
if (firstList.Count != secondList.Count)
{
return false;
}
for (int i = 0; i < firstList.Count; i++)
{
if (firstList[i] != secondList[i])
{
return false;
}
}
return true;
}
}
这个解决方案并不完美,后来实现了一种更优美的方案,性能更好,效果也更佳,本篇文件仅供学习原理参考。新文章《ARCore之路-解决碰撞问题的另一种方法》https://blog.csdn.net/yolon3000/article/details/89949436
主题测试文章,只做测试使用。发布者:风凌恒,转转请注明出处:https://www.doulipark.com/2019/991.html