随着计算机视觉和硬件的进步,AR和VR将成为我们日常生活的一部分。谷歌已经推出了ARCore 1.2.0,面对这一切,我都必须进行尝试和实验。
对于本教程,我们需要一些关于使用Unity的基本知识。对于没有接受过正式Unity学习的我来说,我的第一个任务是寻找合适的在线教程。在掌握了这个工具后,我就可以向更高一阶前进。至于ARCore相关的教程内容似乎非常稀少,似乎有在线学习网站提供课程,但我不确定他们的水平,另外,你需要C#的基本知识。
同时需要注意的是,下面的信息都是基于我个人的理解、知识和经验。我本人也是在不断的学习之中。如果你愿意分享你的想法与经验,我会很高兴。另外,你同时需要一台支持ARCore的设备,我目前正在使用的是Pixel 2。
ARCore主要分为三个部分:理解环境、运动追踪和照明估计。
我们将开始开发我们的第一款应用程序,但在开始之前,我们先准备相应的工具,即便你没有所需的设备,你同样可以继续阅读本文,只是你无法开始构建和部署应用。
在Unity中创建一个新应用,然后将其命名为ARCoreWorld。完成后,我们需要导入我们下载的Unity文件,如要导入,请依次选择Assets → Import Package → Custom Package,选择文件并单击导入。系统应该显示正在导入的项目,在这里,我们将导入所有内容,然后添加预制件(1) ARCore Device和(2) Environmental Light。所有预制件都可以在收藏夹下找到:左下角Project栏中的All Prefabs。
在这种情况下,应用程序属于可构建状态,但要为安卓构建,我们需要切换平台。要访问构建设置,请依次选择从File → Build Settings。选择Android,然后点击Switch Platform。在完成后,点击Player Settings。在这里我们将做出以下更改:在这里我们将提供一个有效的包名称,将最低API级别设置为Android API 24,并且将该目标设置为higest installed。另外,请禁用多线程渲染。在XR设置中,我们将启用ARCore支持。
此时,作为骨架的代码已经准备就绪,但现在运行应用程序只会显示摄像头的实际视图。
点云是一个容器,其包含一组关于现实世界的3D数据点信息,它们用于推导关于真实世界的信息和框架的组成,以便通过虚拟对象来进行增强。
我们需要创建一个材质,材质允许我们为3D模型提供纹理或颜色信息。创建一个名为PointMaterial的材质,并将着色器类型指定为ARCore→PointCloud,我将大小改为15,颜色则是红色。
之后,我们将需要创建一个可以使用这种材质的对象。为此,我们创建一个立方体,然后将材质应用在立方体上。最后,我们需要添加一个名为Pointcloud Visualizer的组件。当前状态下的应用程序将看起来应该是这样。
我们现在已经准备好了舞台。现在我们要把虚拟对象叠加在现实世界中。这个对象是暗黑破坏神。你可以从指定链接中获取模型。下面我们将模型导入Unity,创建一个名为Diablo的文件夹,并将OBJ文件拖放到这里。导入模型后,你会注意到它只是模型的线框,没有纹理。不用担心,模型的开发者同时提供了所需的纹理,但这是DDS格式,所以我们需要将其转换为TGA。你可以采用任何在线工具执行转换。完成后,将TGA文件拖放到Unity。要将TGA文件用作纹理,我们需要从中创建一个材质。创建一个新材质,并将着色器选择为“Unlit/Texture(不亮/纹理)”,然后使用TGA文件。现在将新材质拖放到Diablo模型中,这时你会看到它从灰度模型改变为我们所设想的模型。为了避免模型过大,我减少了尺寸,你可以选择模型,并将比例因子从1减小到0.15,或者按照你的需求进行缩放。完成后,从Diablo模型创建一个预制件,稍后我们将在渲染器上使用它。
现在我们需要查看绘制虚拟对象的平面。为此,我们可以使用Plane Generator和Plane Visualiser组件。
在以后,我们将会把点云立方体的大小减少至5个单位。
下面我们来一点编程。我们已经有了平面,应用纹理后的模型,接下来我们需要将暗黑破坏神叠加在现实世界之中。
创建一个空白对象,并将其命名为DiabloRenderer。现在添加一个新的C#脚本,更新方法应该看起来像这样:
```c#
public GameObject DetectedPlanePrefab;
public GameObject DiabloGameObject;
void Update()
{
_UpdateApplicationLifecycle();
// Hide snackbar when currently tracking at least one plane
Session.GetTrackables(m_AllPlanes);
bool showSearchingUI = true;
for (int i = 0; i < m_AllPlanes.Count; i++)
{
if (m_AllPlanes[i].TrackingState == TrackingState.Tracking)
{
showSearchingUI = false;
break;
}
}
// If the player has not touched the screen, we are done with this update
Touch touch;
if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return;
}
// Raycast against the location the player touched to search for planes
TrackableHit hit;
TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon |
TrackableHitFlags.FeaturePointWithSurfaceNormal;
if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit))
{
// Use hit pose and camera pose to check if hittest is from the
// back of the plane, if it is, no need to create the anchor
if ((hit.Trackable is DetectedPlane) &&
Vector3.Dot(FirstPersonCamera.transform.position - hit.Pose.position,
hit.Pose.rotation * Vector3.up) < 0)
{
Debug.Log("Hit at back of the current DetectedPlane");
}
else
{
var diabloGameObj = Instantiate(DiabloGameObject, hit.Pose.position, hit.Pose.rotation);
diabloGameObj.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
diabloGameObj.transform.parent = anchor.transform;
}
}
}
```
首先,我们需要公开三个我们需要链接的变量:
1. FirstPersonCamera:这是可以在ARCore设备上找到的摄像头,后者则允许我们浏览虚拟对象。
2. DetectedPlanePreFab:这将用于渲染虚拟对象的平面检测。
3. DiabloGameObject:这是Diablo模型的预制件。
```c#
if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return;
}
```
该函数将检查我们是否点击了屏幕,如果没有,则无绘制。
```c#
if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit))
```
一旦我们触碰了屏幕,上面的函数将从屏幕坐标获取输入,然后将其放到虚拟物体叠加在真实世界上的位置。
```c#
var diabloGameObj = Instantiate(DiabloGameObject, hit.Pose.position, hit.Pose.rotation);
diabloGameObj.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
diabloGameObj.transform.parent = anchor.transform;
```
在现实世界中,暗黑破坏神将放置在由hit.Pose.position定义的位置上。接下来,为了将模型附加到现实世界中的某个位置(即使我们试图环绕模型走动),我们需要确保将虚拟对象锚定到创建该模型的Pose。因此,我们创建一个Anchor,并将其分配给游戏对象的父级。
我们已经做出了大量的改动,下面我们看看具体的效果吧:

为了方便截图,我们将大小减少至0.05。
我们可以360度地浏览虚拟对象,详细参见下面的视频。
[https://v.qq.com/iframe/player.html?vid=p0655q4lddx&auto=0](https://v.qq.com/iframe/player.html?vid=p0655q4lddx&auto=0)
我们已经通过这个ARCore示例进行了讲解,我将继续努力,并尝试进一步地优化应用。