ARFoundation之路-平面管理

  平面检测是很多AR应用的基础,无论是ARKit还是ARCore,都提供平面检测功能。同时,平面也是可跟踪对象,在前几节中我们知道,ARFoundation使用ARPlaneManager管理器来管理平面。

(一)平面检测管理

  AR中检测平面的原理:ARFoundation对摄像机获取的图像进行处理,分离图像中的特征点(这些特征点往往都是图像中明暗、强弱、颜色变化较大的点),利用VIO和IMU跟踪这些特征点的三维空间信息,在跟踪过程中,对特征点信息进行处理,并尝试用空间中位置相近或者符合一定规律的特征点构建平面,如果成功就是检测出了平面。平面有其位置、方向和边界信息,ARPlaneManager负责如何检测平面以及管理这些检测出来的平面,但它并不负责渲染平面。

  在ARPlaneManager中,我们可以设置平面检测的方式,如水平平面(Horizontal)、垂直平面(Vertical)、水平平面&垂直平面(Everything)或者不检测平面(Nothing),因为检测平面也是一个消耗性能的工作,所以根据应用需要选择合适的检测方式可以优化应用性能,如下图所示。

在这里插入图片描述
  ARPlaneManager每帧都会进行平面检测,会添加新检测到的平面、更新现有平面、移除过时的平面。当一个新的平面被检测到时,ARPlaneManager会实例化一个平面Prefab来表示该平面,如果开发中ARPlaneManager的Plane Prefab属性没有对赋值,ARPlaneManager将会实例化一个空对象,并在这个空对象上挂载ARPlane组件,ARPlane组件包含了该平面的相关信息数据。

  ARPlaneManager组件还有一个planesChanged事件,开发人员可以注册这个事件,以便在平面发生改变时进行相应处理。

(二)可视化平面

  在ARFoundation中,ARPlaneManager并不负责平面的可视化渲染,而由其Plane Prefab属性指定的预制体负责。在前文中,我们新建了一个AR Default Plane对象作为预制体,该预制体上挂载了如下图所示组件。

在这里插入图片描述
  AR Plane组件负责该平面各类属性事宜,如是否在移除平面时销毁该实例化对象,控制可划分为同一平面的特征点的阀值(上图中红框属性,只有偏差在这个阀值内的特征点才可归属为同一平面,这影响平面检测。);AR Plane Mesh Visualizer该组件主要是从边界特征点和其他特征点三角化生成一个平面网格,有这个平面网格后自然就可以使用Mesh Renderer采用合适材质渲染出来了;默认平面预制体还有一个Line Renderer,它负责渲染平面可视化后的边界连线。所以使用默认平面预制体可视已检测到的平面如下图所示。
在这里插入图片描述

(三)个性化可视平面

  对已检测到的平面默认的可视化显得有些生硬和突兀,有时我们需要更加友好的界面,这时我们就需要对已检测到的平面定制我们自己个性的可视方案。

  为达到更好的视觉效果,处理的思路如下:
  1)、不显示黑色边框;
  2)、重新制作一个渲染材质和Shader脚本,纹理我们使用半透明的PNG,Shader脚本渲染这个半透明的纹理,将纹理空白区域都镂空;
  3)、编写一个渐隐的脚本,让边缘的纹理渐隐,达到更好的视觉过渡。

  按照以上思路,我们直接对AR Default Plane预制体进行改造。
  1)、删除AR Default Plane预制体上的Line Renderer组件;
  2)、编写如下所示Shader,制作一张PNG半透明纹理,并新建一个利用这个Shader的材质。

Shader "Unlit/FeatheredPlaneShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_TexTintColor("Texture Tint Color", Color) = (1,1,1,1)
		_PlaneColor("Plane Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "RenderType"="Transparent" "Queue"="Transparent" }
		LOD 100
		Blend SrcAlpha OneMinusSrcAlpha
		ZWrite Off

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 uv2 : TEXCOORD1;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 uv2 : TEXCOORD1;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _TexTintColor;
			fixed4 _PlaneColor;
			float _ShortestUVMapping;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.uv2 = v.uv2;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv) * _TexTintColor;
				col = lerp( _PlaneColor, col, col.a);
				// Fade out from as we pass the edge.
				// uv2.x stores a mapped UV that will be "1" at the beginning of the feathering.
				// We fade until we reach at the edge of the shortest UV mapping.
				// This is the remmaped UV value at the vertex.
				// We choose the shorted one so that ll edges will fade out completely.
				// See ARFeatheredPlaneMeshVisualizer.cs for more details.
				col.a *=  1-smoothstep(1, _ShortestUVMapping, i.uv2.x);
				return col;
			}
			ENDCG
		}
	}
}

  在这个Shader中,有三个参数,Texture即为纹理,将我们制作的纹理赋给它,TextureTintColor为纹理显示,我们要让透明的十字星号显示出来,Alpha设置为220,PlaneColor为平面的背景色,这里我们不要背景色,Alpha设置为0,如下图所示。
在这里插入图片描述
  3)、新建一个C#脚本文件,命名为ARFeatheredPlaneMeshVisualizer.cs,编写如下代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

/// <summary>
/// This plane visualizer demonstrates the use of a feathering effect
/// at the edge of the detected plane, which reduces the visual impression
/// of a hard edge.
/// </summary>
[RequireComponent(typeof(ARPlaneMeshVisualizer), typeof(MeshRenderer), typeof(ARPlane))]
public class ARFeatheredPlaneMeshVisualizer : MonoBehaviour
{
    [Tooltip("The width of the texture feathering (in world units).")]
    [SerializeField]
    float m_FeatheringWidth = 0.2f;

    /// <summary>
    /// The width of the texture feathering (in world units).
    /// </summary>
    public float featheringWidth
    { 
        get { return m_FeatheringWidth; }
        set { m_FeatheringWidth = value; } 
    }

    void Awake()
    {
        m_PlaneMeshVisualizer = GetComponent<ARPlaneMeshVisualizer>();
        m_FeatheredPlaneMaterial = GetComponent<MeshRenderer>().material;
        m_Plane = GetComponent<ARPlane>();
    }

    void OnEnable()
    {
        m_Plane.boundaryChanged += ARPlane_boundaryUpdated;
    }

    void OnDisable()
    {
        m_Plane.boundaryChanged -= ARPlane_boundaryUpdated;
    }

    void ARPlane_boundaryUpdated(ARPlaneBoundaryChangedEventArgs eventArgs)
    {
        GenerateBoundaryUVs(m_PlaneMeshVisualizer.mesh);
    }

    /// <summary>
    /// Generate UV2s to mark the boundary vertices and feathering UV coords.
    /// </summary>
    /// <remarks>
    /// The <c>ARPlaneMeshVisualizer</c> has a <c>meshUpdated</c> event that can be used to modify the generated
    /// mesh. In this case we'll add UV2s to mark the boundary vertices.
    /// This technique avoids having to generate extra vertices for the boundary. It works best when the plane is 
    /// is fairly uniform.
    /// </remarks>
    /// <param name="mesh">The <c>Mesh</c> generated by <c>ARPlaneMeshVisualizer</c></param>
    void GenerateBoundaryUVs(Mesh mesh)
    {
        int vertexCount = mesh.vertexCount;

        // Reuse the list of UVs
        s_FeatheringUVs.Clear();
        if (s_FeatheringUVs.Capacity < vertexCount) { s_FeatheringUVs.Capacity = vertexCount; }

        mesh.GetVertices(s_Vertices);

        Vector3 centerInPlaneSpace = s_Vertices[s_Vertices.Count - 1];
        Vector3 uv = new Vector3(0, 0, 0);
        float shortestUVMapping = float.MaxValue;

        // Assume the last vertex is the center vertex.
        for (int i = 0; i < vertexCount - 1; i++)
        {
            float vertexDist = Vector3.Distance(s_Vertices[i], centerInPlaneSpace);

            // Remap the UV so that a UV of "1" marks the feathering boudary.
            // The ratio of featherBoundaryDistance/edgeDistance is the same as featherUV/edgeUV.
            // Rearrange to get the edge UV.
            float uvMapping = vertexDist / Mathf.Max(vertexDist - featheringWidth, 0.001f);
            uv.x = uvMapping;

            // All the UV mappings will be different. In the shader we need to know the UV value we need to fade out by.
            // Choose the shortest UV to guarentee we fade out before the border.
            // This means the feathering widths will be slightly different, we again rely on a fairly uniform plane.
            if (shortestUVMapping > uvMapping) { shortestUVMapping = uvMapping; }

            s_FeatheringUVs.Add(uv);
        }

        m_FeatheredPlaneMaterial.SetFloat("_ShortestUVMapping", shortestUVMapping);

        // Add the center vertex UV
        uv.Set(0, 0, 0);
        s_FeatheringUVs.Add(uv);

        mesh.SetUVs(1, s_FeatheringUVs);
        mesh.UploadMeshData(false);
    }

    static List<Vector3> s_FeatheringUVs = new List<Vector3>();

    static List<Vector3> s_Vertices = new List<Vector3>();

    ARPlaneMeshVisualizer m_PlaneMeshVisualizer;

    ARPlane m_Plane;

    Material m_FeatheredPlaneMaterial;
}

  将ARFeatheredPlaneMeshVisualizer挂载到AR Default Plane预制体上,完成之后应该如下图所示:
在这里插入图片描述
  编译运行,效果如下所示,视觉效果要好很多了。
在这里插入图片描述

  注:本文Shader与ARFeatheredPlaneMeshVisualizer.cs脚本均取自参考代码工程,为方便读者,我已将图片、Shader、脚本分离出来,读者也可以在 这里 下载,直接就可以在工程里使用。

参考代码

arfoundation-samples arfoundation-samples

ARFoundation是一个用于在移动设备上创建现实增强应用程序的开发框架。人脸跟踪编程是ARFoundation系列教程中的一部分。 人脸跟踪是通过相机捕捉实时视频流并使用AR技术来检测和跟踪人脸的过程。这个过程涉及到计算机视觉和人工智能的技术,可以在移动设备上实时地识别人脸,进而应用各种效果和交互。 在ARFoundation系列教程中学习人脸跟踪编程,你将学习如何使用ARFoundation和Unity引擎来构建具有人脸追踪功能的应用程序。教程会向你介绍如何在Unity中创建一个AR项目,并使用ARFoundation的API来实现人脸追踪功能。 首先,你需要在Unity中导入ARFoundation库并设置相机权限。然后,你可以创建一个3D模型来代表人脸,并将其与人脸跟踪的数据进行关联。在跟踪开始后,你可以通过获取人脸的特征点信息和姿势来实时地更新模型的位置和角度。 此外,你还可以根据人脸的表情特征,例如眨眼、微笑等,来触发应用程序中的效果或交互。例如,你可以通过检测到用户眨眼的动作来实现快门的触发,拍摄照片或录制视频。 通过学习ARFoundation系列教程中的人脸跟踪编程,你将能够掌握如何使用AR技术在移动设备上实现实时人脸跟踪功能。这将为你开发创新的增强现实应用程序提供基础,并且能够为用户提供更加沉浸式和交互性的体验。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_DavidWang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值